【问题标题】:How to mock a service class calling Rest API and getting ResponseEntity as Response如何模拟调用 Rest API 的服务类并将 ResponseEntity 作为响应
【发布时间】:2020-08-17 21:07:35
【问题描述】:

我有以下课程。

@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 Convertor here
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.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<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") 行的调用会产生 NullPointerException。在调试时,我发现在 CallingService.getResponse() 中收到的响应为 null(即 restService.callService(param) 返回 null),因此代码被分解为 convert() 方法。

我尝试了很多代码操作,但没有运气。希望任何人都可以回答这个问题。

【问题讨论】:

  • 能否请您也分享一下您从测试课程中导入的内容?
  • @SSK 使用 Junit 导入编辑了类。
  • 您正在使用Junit4Junit5 的组合。我在下面添加了我的答案。希望这会奏效。

标签: java spring-boot junit mockito


【解决方案1】:

您不能同时使用@RunWith(MockitoJUnitRunner.class)@SpringBootTest。他们都在设置 JUnit 规则,您只能激活一个规则。

你需要使用 Spring 注解。而不是 @Mock 使用 @MockBean 这样 Spring 可以正确地将其注入服务。

@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<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
}
}

或者,您可以只使用 Mockito,因为我认为您实际上不需要为这个测试初始化​​ SpringContext。

@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<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
}
}

【讨论】:

  • 只有使用 @RunWith(MockitoJUnitRunner.class) 才能给出 restService 的空值。它不是在嘲笑 bean。
  • 具体在哪里?在测试中还是在CallingService
  • 在测试类CallingServiceTest中。
  • 您可以尝试拨打MockitoAnnotations.initMocks(CallingServiceTest.class) 吗?在@Before 方法中。 MockitoJUnitRunner 应该已经在处理注释了。
  • 和以前一样。 restService 的 null 值。
【解决方案2】:

根据您的导入,您正在使用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<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
}
}

如果您想使用Junit4 运行,请将@Test 的导入从import org.junit.jupiter.api.Test 更改为import org.junit.Test

任何一种解决方案都可以。

【讨论】:

  • 使用 Junit5 的设置会出现以下错误。 java.lang.NoSuchMethodError: org.junit.platform.commons.support.AnnotationSupport.findAnnotation(Ljava/util/Optional;Ljava/lang/Class;)Ljava/util/Optional;在 org.mockito.junit.jupiter.MockitoExtension.retrieveAnnotationFromTestClasses(MockitoExtension.java:183)
  • 如果您遇到该错误,只需使用JUnit 4 运行您的测试用例。右键单击测试类 -> 运行方式 -> 运行配置 -> 选择 JUnit 4
  • 其实我只想用 Junit5 运行测试。相同的代码配置适用于没有 restService 的其他测试类。
  • 除了restService.callService()的响应之外,每个方法调用都被完美地模拟了
【解决方案3】:

我终于通过结合上述两个答案的风格找到了解决方法。

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);
// mocking converter here
dto = service.getResponse("param");
//assert conditions here onwards
}
}

【讨论】:

    猜你喜欢
    • 2020-02-04
    • 2017-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    • 1970-01-01
    • 2023-02-07
    相关资源
    最近更新 更多