如何模拟调用 Rest API 并获取 ResponseEntity 作为响应的服务类。

huangapple go评论78阅读模式
英文:

How to mock a service class calling Rest API and getting ResponseEntity as Response

问题

我有以下的类。

@Service
class RestService {
    @Autowired
    private RestTemplate restTemplate;

    public ResponseEntity<String> callService(String param) {
        return restTemplate.exchange(....);
    }
}
@Service
class CallingService {
    @Autowired
    private RestService restService;

    public ResponseDTO getResponse(String param) {
        ResponseEntity<String> response = restService.callService(param);
        ResponseDTO responseDTO = convert(response) // 这里是 JSON 转换器
        return responseDTO;
    }
}

现在我想为 CallingService 类编写测试类。

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;

import javax.xml.datatype.DatatypeConfigurationException;

import org.apache.cxf.helpers.IOUtils;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
class CallingServiceTest {
    @InjectMocks
    private CallingService service;
    @Mock
    private RestService restService;

    @Test
    public void testGetResponse() {
        ResponseDTO dto = createDummyObject();

        HttpHeaders header = new HttpHeaders();
        header.setContentType(MediaType.APPLICATION_JSON);

        ResponseEntity<String> responseEntity = new ResponseEntity<>("response message", header,
                HttpStatus.OK);

        when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
        // mocking converter here
        dto = service.getResponse("param");
        //assert conditions here onwards
    }
}

service.getResponse("param") 行的调用会导致空指针异常。在调试中,我发现 CallingService.getResponse() 中接收的响应为 null(即 restService.callService(param) 返回了 null),因此代码中断到了 convert() 方法。

我尝试了很多代码操作,但没有成功。希望有人能对此有答案。

英文:

I have below classes.

@Service
class RestService {
@Autowired
private RestTemplate restTemplate;
public ResponseEntity&lt;String&gt; callService(String param) {
return restTemplate.exchange(....);
}
}

@Service
class CallingService{
@Autowired
private RestService restService;
public ResponseDTO getResponse(String param) {
ResponseEntity&lt;String&gt; response = restService.callService(param);
ResponseDTO responseDTO = convert(response)// JSON Convertor here
return responseDTO;
}
}

Now I want to write test class for CallingService class.

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import javax.xml.datatype.DatatypeConfigurationException;
import org.apache.cxf.helpers.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
class CallingServiceTest{
@InjectMocks
private CallingService service;
@Mock
private RestService restService;
@Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity&lt;String&gt; responseEntity = new ResponseEntity&lt;&gt;(&quot;response message&quot;, header, 
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse(&quot;param&quot;);
//assert conditions here onwards
}
}

The invocation at line service.getResponse("param") gives NullPointerException. On debugging I found that response received in CallingService.getResponse() is null (i.e. restService.callService(param) returned null) and hence code broken into convert() method.

I tried with a lot of code manipulations but no luck. Hope anyone might have answer for this.

答案1

得分: 1

我终于通过结合上面两个答案的方法找到了一个解决方法

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.apache.cxf.helpers.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class CallingServiceTest{
@InjectMocks
private CallingService service;
@Mock
private RestService restService;
@BeforeEach
public void setup(){
MockitoAnnotations.init(this);
}

@Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();

HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);

ResponseEntity<String> responseEntity = new ResponseEntity<>(response message, header, HttpStatus.OK);

     when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// 在这里进行转换器的模拟
dto = service.getResponse("param");
// 从这里开始进行断言条件
}
}
英文:

I finally found a workaround by combining flavours of above two answers.

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.apache.cxf.helpers.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class CallingServiceTest{
@InjectMocks
private CallingService service;
@Mock
private RestService restService;
@BeforeEach
public void setup(){
MockitoAnnotations.init(this);
}
@Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity&lt;String&gt; responseEntity = new ResponseEntity&lt;&gt;   (&quot;response message&quot;, header, HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse(&quot;param&quot;);
//assert conditions here onwards
}
}

答案2

得分: 0

不能同时使用@RunWith(MockitoJUnitRunner.class)@SpringBootTest。它们都在设置JUnit规则,而你只能有一个规则生效。

你需要使用Spring注解。代替@Mock,使用@MockBean,这样Spring可以正确地将其注入到服务中。

@SpringBootTest
class CallingServiceTest {
    @Autowired
    private CallingService service;
    @MockBean
    private RestService restService;

    @Test
    public void testGetResponse() {
        ResponseDTO dto = createDummyObject();

        HttpHeaders header = new HttpHeaders();
        header.setContentType(MediaType.APPLICATION_JSON);

        ResponseEntity<String> responseEntity = new ResponseEntity<>("response message", header,
                HttpStatus.OK);

        when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
        // 在这里进行转换器的模拟
        dto = service.getResponse("param");
        // 在此之后进行断言条件
    }
}

或者,你可以只使用Mockito,因为我认为你不需要为这个测试初始化Spring上下文。

@RunWith(MockitoJUnitRunner.class)
class CallingServiceTest {
    @InjectMocks
    private CallingService service;
    @Mock
    private RestService restService;

    @Test
    public void testGetResponse() {
        ResponseDTO dto = createDummyObject();

        HttpHeaders header = new HttpHeaders();
        header.setContentType(MediaType.APPLICATION_JSON);

        ResponseEntity<String> responseEntity = new ResponseEntity<>("response message", header,
                HttpStatus.OK);

        when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
        // 在这里进行转换器的模拟
        dto = service.getResponse("param");
        // 在此之后进行断言条件
    }
}
英文:

You can't use both @RunWith(MockitoJUnitRunner.class) and @SpringBootTest. They're both setting up JUnit Rules and you can only have one rule active.

You need to use Spring annotations. Instead of @Mock use @MockBean so Spring can properly inject it to the service.

@SpringBootTest
class CallingServiceTest{
@Autowired
private CallingService service;
@MockBean
private RestService restService;
@Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity&lt;String&gt; responseEntity = new ResponseEntity&lt;&gt;(&quot;response message&quot;, header, 
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse(&quot;param&quot;);
//assert conditions here onwards
}
}

Alternatively, you can use just Mockito since you don't really need to initialize SpringContext for this test I think.

@RunWith(MockitoJUnitRunner.class)
class CallingServiceTest{
@InjectMocks
private CallingService service;
@Mock
private RestService restService;
@Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity&lt;String&gt; responseEntity = new ResponseEntity&lt;&gt;(&quot;response message&quot;, header, 
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse(&quot;param&quot;);
//assert conditions here onwards
}
}

答案3

得分: 0

根据您的导入,您正在同时使用Junit4Junit5的组合。这不会正常工作。

使用Junit5运行的步骤

  1. 将您的导入更改为org.junit.jupiter.api.*
  2. @Before更改为@BeforeEach
  3. 删除@SpringBootTest,在Junit5下不需要。
  4. @RunWith(MockitoJUnitRunner.class)更改为@ExtendWith(MockitoExtension.class)

请查看下面的更改后的代码

@ExtendWith(MockitoExtension.class)
class CallingServiceTest{
@InjectMocks
private CallingService service;
@Mock
private RestService restService;

@Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();

HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);

ResponseEntity&lt;String&gt; responseEntity = new ResponseEntity&lt;&gt;(&quot;response message&quot;, header, 
HttpStatus.OK);

when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
 dto = service.getResponse(&quot;param&quot;);
//assert conditions here onwards
}
}

如果要使用Junit4运行,请将@Test的导入更改为import org.junit.Test,而不是import org.junit.jupiter.api.Test

这两种解决方案中的任何一种都可以工作。

英文:

Based on your import you are using a combination of Junit4 and Junit5.
This will not work properly.

Steps to run with Junit5

  1. Change your imports to org.junit.jupiter.api.*
  2. Change @Before to @BeforeEach.
  3. Remove @SpringBootTest not required with Junit5
  4. Change @RunWith(MockitoJUnitRunner.class) to @ExtendWith(MockitoExtension.class)

Please find the changed code below

@ExtendWith(MockitoExtension.class)
class CallingServiceTest{
@InjectMocks
private CallingService service;
@Mock
private RestService restService;
@Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity&lt;String&gt; responseEntity = new ResponseEntity&lt;&gt;(&quot;response message&quot;, header, 
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse(&quot;param&quot;);
//assert conditions here onwards
}
}

If you want to run with Junit4 change your import for @Test to import org.junit.Test from import org.junit.jupiter.api.Test

Either one of the solutions will work.

huangapple
  • 本文由 发表于 2020年8月18日 05:07:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63458665.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定