web-dev-qa-db-de.com

Wie kann ich Methoden der @ InjectMocks-Klasse simulieren?

Zum Beispiel habe ich einen Handler:

@Component
public class MyHandler {

  @AutoWired
  private MyDependency myDependency;

  public int someMethod() {
    ...
    return anotherMethod();
  }

  public int anotherMethod() {...}
}

zum Testen möchte ich so etwas schreiben:

@RunWith(MockitoJUnitRunner.class}
class MyHandlerTest {

  @InjectMocks
  private MyHandler myHandler;

  @Mock
  private MyDependency myDependency;

  @Test
  public void testSomeMethod() {
    when(myHandler.anotherMethod()).thenReturn(1);
    assertEquals(myHandler.someMethod() == 1);
  }
}

Es ruft jedoch anotherMethod() auf, wenn ich versuche, es zu verspotten. Was soll ich mit myHandler machen, um seine Methoden zu verspotten?

40
Vova Yatsyk

Der Grund für das Verspotten von MyHandler-Methoden kann zuallererst der folgende sein: Wir testen bereits anotherMethod() und haben eine komplexe Logik. Warum müssen wir sie erneut testen (wie ein Teil von someMethod()), wenn wir nur verify aufrufen können, das aufgerufen wird?
Wir können es durch: 

@RunWith(MockitoJUnitRunner.class}
class MyHandlerTest {

  @Spy  
  @InjectMocks  
  private MyHandler myHandler;  

  @Mock  
  private MyDependency myDependency;  

  @Test  
  public void testSomeMethod() {  
    doReturn(1).when(myHandler).anotherMethod();  
    assertEquals(myHandler.someMethod() == 1);  
    verify(myHandler, times(1)).anotherMethod();  
  }  
}  

Hinweis: Im Falle eines 'Spionage'-Objekts müssen wir doReturn anstelle von thenReturn verwenden (kleine Erklärung ist hier )

74
Vova Yatsyk

In Ihrem Code testen Sie MyHandler überhaupt nicht. Sie möchten das, was Sie testen, nicht verspotten, sondern die eigentlichen Methoden aufrufen. Wenn MyHandler Abhängigkeiten hat, machen Sie sich über sie lustig.

Etwas wie das:

public interface MyDependency {
  public int otherMethod();
}

public class MyHandler {
  @AutoWired
  private MyDependency myDependency;

  public void someMethod() {
    myDependency.otherMethod();
  }
}

Und im Test:

private MyDependency mockDependency;
private MyHandler realHandler;

@Before
public void setup() {
   mockDependency = Mockito.mock(MyDependency.class);
   realHandler = new MyHandler();
   realhandler.setDependency(mockDependency); //but you might Springify this 
}

@Test
public void testSomeMethod() {

  //specify behaviour of mock
  when(mockDependency.otherMethod()).thenReturn(1);

  //really call the method under test
  realHandler.someMethod();
}

Es geht darum, die zu testende Methode wirklich aufzurufen, aber mögliche Abhängigkeiten zu simulieren (z. B. Aufrufmethode anderer Klassen).

Wenn diese anderen Klassen Teil Ihrer Anwendung sind, haben sie eigene Komponententests.

NOTEder obige Code könnte mit mehr Anmerkungen verkürzt werden, aber ich wollte es zur Erklärung noch deutlicher machen (und ich kann mich auch nicht erinnern, was die Anmerkungen sind :) 

0
NickJ