2016-08-23 8 views
2

Ich habe einige Schwierigkeiten, die notwendigen Abhängigkeiten für einige Komponententests zu verspotten. Ich vermute, dass ich nicht richtig verstehe, wie man den Mockito verwendet, wenn(). ThenReturn().Wenn when (methodX) .thenReturn (resultY) aufgerufen wird, wird methodX aufgerufen?

Hier ist (hoffentlich die relevant) Teil des Codes für den Dienst möchte ich Test:

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

} 

Mein Verständnis ist, dass meine Unit-Test für diese Methode wie folgt aussehen sollte:

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()); 
    } 
} 

Wenn ich meinen Test ausführen, bekomme ich eine NullPointerException in der Methode "terminationConverterHelper", wenn ich versuche, "dateDto.getCustomer()" aufzurufen, was verständlicherweise leer ist. Da der Code jetzt ausgeführt wird, scheint es mir, dass ich einen InputStream, der einen vollständig gültigen Termin enthält, in meiner mockHttpResponse zurückgeben muss. Da ich when(appointmentService.validateResponse(mockHttpResponse, mockUriInfo)).thenReturn(new Appointment()); habe, sollte das den validateResponse Aufruf nicht abholen und das leere Appointment-Objekt zurückgeben, anstatt tatsächlich validateResponse auszuführen?

Als Referenz ist hier der Stack-Trace auf der Ausnahme - Zeile 140 von AppointmentServiceTest ist die when Anweisung oben. Linie 176 des AppointmentsService ist die JacksonJsonUtility.readValue(response.getInputStream(), AppointmentWrapper.class); Linie am Anfang der validateResponse Methode:

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

Können Sie den StackTrace für die NPE veröffentlichen? –

+0

@ Code-Apprentice Stack-Trace wurde hinzugefügt – DanHam

Antwort

3

ich das Problem denken könnte sein, dass die when Aussage versucht, gegen eine bestimmte Instanz zu entsprechen und versagt. Um mit Mockito Unit Tests zu beginnen, sollten Sie die spezifischen Werte in Ihren when Anweisungen durch Mockito Platzhalter ersetzen, die "alles" darstellen. Zum Beispiel:

when(mockDataConnector.getAppointmentById(Mockito.anyString())). 
    thenReturn(mockHttpResponse); 

when(appointmentService.validateResponse(Mockito.any(HttpResponse.class), Mockito.any(UriInfo.class))). 
     thenReturn(new Appointment()); 

Wenn Sie die grundlegenden Komponententests arbeiten haben, können Sie fügen Sie dann spezifischere Unit-Tests in.

aktualisieren

Ich merke jetzt, dass Sie die AppointmentService Klasse zu testen, versuchen, aber Sie sind auch innerhalb dieser Klasse die validateResponse Methode spöttisch aus. Ich bin nicht genug von einem Mockito-Experten, um zu wissen, ob das völlig falsch ist, aber das ist nicht die Art, wie ich Mockito in meinen Unit-Tests verwende. Die Klasse, die ich teste, wird überhaupt nicht verspottet, stattdessen spotte ich die aufgerufenen Klassen, um das Verhalten der Variablen zu simulieren und zu sehen, wie die getestete Klasse reagiert.

Ich denke, Sie müssen möglicherweise die JacksonJsonUtility Klasse (PowerMock hilft mit statischen Klassen) verspotten und entfernen Sie die Mock auf die validateResponse Methode. Sie müssen die Scheinantwort auf die JacksonJsonUtility.readValue-Methode so erstellen, dass sie alles enthält, was die AppointmentService-Klasse erwartet.

+0

Hat diese Änderung gemacht, aber sieht aus wie ich immer noch diese NullPointerException innerhalb der validateResponse-Methode. Denkst du, das bedeutet, dass ich immer noch versuche, gegen eine bestimmte Instanz zu kommen? – DanHam

+0

Sieht so aus, als würde die Ausnahme auf die validateResponse 'when'-Anweisung geworfen, selbst wenn ich den eigentlichen' terminationService.getAppointment'-Aufruf auskommentiere. Also ich denke das Problem ist mit der 'when' Anweisung, die ich versuche, die 'validateResponse'-Methode selbst auszuführen, anstatt nur die verspottete Antwort zurückzugeben? – DanHam

+0

Dies ist eine ziemlich grundlegende Frage Grundlagen Test Frage, aber möchte ich nicht die 'validateResponse' Methode verspotten, um sicherzustellen, dass ich nur die' getAppointment'-Methode testen? Oder ist es zulässig, mehrere Methoden in denselben Komponententest aufzunehmen, solange sie sich in derselben Klasse befinden? – DanHam

0

Basierend auf this post, ich denke, Spionage der AppointmentService ist der Weg zu gehen, um mich zu ermöglichen, den validateResponse Aufruf zu verspotten. Also hier ist, wie mein Test sieht jetzt:

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()); 
    } 
} 

Ich bin immer noch eine Ausnahme auf dem tatsächlichen appointmentServiceSpy.getAppointment Anruf bekommen, aber ich denke, dass ein anderes Thema ist damit zusammen, wie ich den Spion verwenden. Für die Zwecke der ursprünglichen Frage behebt die Verwendung eines Spions mit einer doReturn().when()-Anweisung das Problem, dass die Methode irrtümlich aufgerufen wird, wenn die Anweisung when().thenReturn() verwendet wird.

Verwandte Themen