英文:
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<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;
}
}
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<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
}
}
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<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
}
}
答案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<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
}
}
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<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
}
}
答案3
得分: 0
根据您的导入,您正在同时使用Junit4
和Junit5
的组合。这不会正常工作。
使用Junit5
运行的步骤
- 将您的导入更改为
org.junit.jupiter.api.*
- 将
@Before
更改为@BeforeEach
。 - 删除
@SpringBootTest
,在Junit5
下不需要。 - 将
@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.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
- Change your imports to
org.junit.jupiter.api.*
- Change
@Before
to@BeforeEach
. - Remove
@SpringBootTest
not required withJunit5
- 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<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
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论