我在模拟某些单元测试的必要依赖项时遇到了一些困难。我怀疑我没有正确理解如何使用Mockito()。thenReturn()。
这是我想要测试的服务的代码(希望是相关的)部分:
@Singleton
public class AppointmentService {
private DataConnector connector;
@Inject
public AppointmentService(DataConnector connector) {
this.connector = connector;
}
public Appointment getAppointment(UriInfo uriInfo, String id) {
HttpResponse response = connector.getAppointment(id);
return validateResponse(response, uriInfo);
}
protected Appointment validateResponse(HttpResponse response, UriInfo uriInfo) {
try {
AppointmentWrapper appointmentWrapper = JacksonJsonUtility.readValue(response.getInputStream(), AppointmentWrapper.class);
if(appointmentWrapper.getAppointment()!=null) {
return appointmentConverterHelper(appointmentWrapper.getAppointment(), uriInfo.getPath());
}
} catch(IOException e) {
throw new ResponseParsingException("IOException: " + e.getMessage());
}
}
protected Appointment appointmentConverterHelper(ExternalAppointmentModel appointmentDto, String uriPath) {
if (appointmentDto == null) {
throw new BrickworkQueryException("Got an invalid appointment object from Brickwork!");
}
Customer customer = new Customer(new Name(appointmentDto.getCustomer().getName()),
appointmentDto.getCustomer().getEmail(),
String.valueOf(appointmentDto.getCustomer().getCode()));
return new Appointment(
appointmentDto.getId(),
customer);
}
}
我的理解是我对这个方法的单元测试应该是这样的:
public class AppointmentServiceTest {
private UriInfo mockUriInfo;
private HttpResponse mockHttpResponse;
@Mock
private DataConnector mockDataConnector;
@InjectMocks
private AppointmentService appointmentService;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockUriInfo();
mockHttpResponse();
}
private void mockUriInfo() {
mockUriInfo = mock(UriInfo.class);
when(mockUriInfo.getRequestUri()).thenReturn(
URI.create("http://localhost:8080"));
}
private void mockHttpResponse() {
mockHttpResponse = mock(HttpResponse.class);
when(mockHttpResponse.getInputStream()).thenReturn(
IOUtils.toInputStream("{\"appointment\":{}}"));
}
@Test
public void testGetAppointment() throws Exception {
when(mockDataConnector.getAppointmentById(Mockito.anyString())).
thenReturn(mockHttpResponse);
when(appointmentService.validateResponse(Mockito.any(HttpResponse.class), Mockito.any(UriInfo.class))).
thenReturn(new Appointment());
appointmentService.getAppointment(mockUriInfo, "12345");
verify(mockDataConnector).getAppointment(Mockito.anyString());
}
}
当我运行我的测试时,在尝试调用appointmentDto.getCustomer()时,在appointmentConverterHelper方法中得到NullPointerException,这可以理解为空。由于代码现在正在运行,在我看来,我需要在mockHttpResponse中返回一个包含完全有效的约会的InputStream。因为我when(appointmentService.validateResponse(mockHttpResponse, mockUriInfo)).thenReturn(new Appointment());
不应该选择validateResponse
调用并返回空的Appointment对象而不是实际运行validateResponse?
作为参考,这里是Exception的堆栈跟踪 - AppointmentServiceTest的第140行是上面的when
语句。 AppointmentsService的第176行是JacksonJsonUtility.readValue(response.getInputStream(), AppointmentWrapper.class);
方法开头的validateResponse
行:
java.lang.NullPointerException
at com.nike.appointmentservice.service.AppointmentsService.validatedResponse(AppointmentsService.java:176)
at com.nike.appointmentservice.service.AppointmentsServiceTest.testGetAppointmentById(AppointmentsServiceTest.java:140)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)```
答案 0 :(得分:3)
我认为问题可能是when
语句试图与特定实例匹配并且失败。要开始使用Mockito单元测试,您应该使用代表“任何内容”的Mockito占位符替换when
语句中的特定值。例如:
when(mockDataConnector.getAppointmentById(Mockito.anyString())).
thenReturn(mockHttpResponse);
when(appointmentService.validateResponse(Mockito.any(HttpResponse.class), Mockito.any(UriInfo.class))).
thenReturn(new Appointment());
当您进行基本单元测试时,您可以添加更具体的单元测试。
<强>更新强>
我现在注意到您正在尝试测试AppointmentService
类,但您也在嘲笑该类中的validateResponse
方法。我不够Mockito专家知道这是否完全不正确,但这不是我在单元测试中使用Mockito的方式。我正在测试的类根本没有被模拟,而是我模拟了它调用的类,以便模拟变量行为并查看测试类如何响应。
我认为您可能需要模拟JacksonJsonUtility
类(powermock帮助静态类)并删除validateResponse
方法上的模拟。您需要构造对JacksonJsonUtility.readValue
方法的模拟响应,使其包含AppointmentService类所期望的所有内容。
答案 1 :(得分:0)
基于this post,我认为监视AppointmentService是允许我嘲笑validateResponse
电话的方法。所以现在我的测试看起来如何:
public class AppointmentServiceTest {
private UriInfo mockUriInfo;
private HttpResponse mockHttpResponse;
@Mock
private DataConnector mockDataConnector;
@Spy
private AppointmentsService appointmentsServiceSpy;
@InjectMocks
private AppointmentService appointmentService;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
appointmentsServiceSpy = spy(appointmentsService);
mockUriInfo();
mockHttpResponse();
}
private void mockUriInfo() {
mockUriInfo = mock(UriInfo.class);
when(mockUriInfo.getRequestUri()).thenReturn(
URI.create("http://localhost:8080"));
}
private void mockHttpResponse() {
mockHttpResponse = mock(HttpResponse.class);
when(mockHttpResponse.getInputStream()).thenReturn(
IOUtils.toInputStream("{\"appointment\":{}}"));
}
@Test
public void testGetAppointment() throws Exception {
when(mockDataConnector.getAppointmentById(Mockito.anyString())).
thenReturn(mockHttpResponse);
doReturn(new Appointment()).when(appointmentsServiceSpy).validateResponse(Mockito.any(HttpResponse.class), Mockito.any(UriInfo.class));
appointmentServiceSpy.getAppointment(mockUriInfo, "12345");
verify(mockDataConnector).getAppointmentById(Mockito.anyString());
}
}
我仍然在实际的appointmentServiceSpy.getAppointment
电话上获得例外,但我认为这是一个与我如何使用间谍相关的单独问题。出于原始问题的目的,使用带有doReturn().when()
语句的间谍可以解决使用when().thenReturn()
语句时错误调用的方法问题。