Ich bin neu bei Mockito.
Wie kann ich mit Mockito überprüfen, ob someMethod
genau einmal aufgerufen wurde, nachdem foo
aufgerufen wurde?
public class Foo
{
public void foo(){
Bar bar = new Bar();
bar.someMethod();
}
}
Ich möchte den folgenden Bestätigungsanruf tätigen:
verify(bar, times(1)).someMethod();
dabei ist bar
eine Scheininstanz von Bar
.
Wenn Sie die Bar-Instanz oder eine Fabrik einfügen, in der die Bar-Instanz erstellt wird (oder eine der anderen 483 Möglichkeiten, dies durchzuführen), haben Sie den erforderlichen Zugriff, um den Test durchzuführen.
Fabrik Beispiel:
Gegeben eine Foo-Klasse wie folgt geschrieben:
public class Foo {
private BarFactory barFactory;
public Foo(BarFactory factory) {
this.barFactory = factory;
}
public void foo() {
Bar bar = this.barFactory.createBar();
bar.someMethod();
}
}
in Ihrer Testmethode können Sie eine BarFactory folgendermaßen injizieren:
@Test
public void testDoFoo() {
Bar bar = mock(Bar.class);
BarFactory myFactory = new BarFactory() {
public Bar createBar() { return bar;}
};
Foo foo = new Foo(myFactory);
foo.foo();
verify(bar, times(1)).someMethod();
}
Bonus: Dies ist ein Beispiel, wie TDD Ihren Code gestalten kann.
Die klassische Antwort lautet: "Sie tun es nicht." Sie testen die öffentliche API von Foo
, nicht deren Interna.
Gibt es ein Verhalten des Foo
-Objekts (oder, weniger gut, eines anderen Objekts in der Umgebung), das von foo()
betroffen ist? Wenn ja, teste das. Und wenn nicht, was macht die Methode?
Wenn Sie keine DI oder Factories verwenden möchten. Sie können Ihre Klasse auf etwas knifflige Weise umgestalten:
public class Foo {
private Bar bar;
public void foo(Bar bar){
this.bar = (bar != null) ? bar : new Bar();
bar.someMethod();
this.bar = null; // for simulating local scope
}
}
Und deine Testklasse:
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock Bar barMock;
Foo foo;
@Test
public void testFoo() {
foo = new Foo();
foo.foo(barMock);
verify(barMock, times(1)).someMethod();
}
}
Dann wird die Klasse, die Ihre foo-Methode aufruft, folgendermaßen vorgehen:
public class thirdClass {
public void someOtherMethod() {
Foo myFoo = new Foo();
myFoo.foo(null);
}
}
Wenn Sie die Methode auf diese Weise aufrufen, müssen Sie die Bar-Klasse nicht in eine andere Klasse importieren, die Ihre foo-Methode aufruft.
Der Nachteil ist natürlich, dass Sie dem Anrufer die Einstellung des Bar-Objekts gestatten.
Ich hoffe es hilft.
Lösung für Ihren Beispielcode mit PowerMockito.whenNew
FooTest.Java
package foo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
//Both @PrepareForTest and @RunWith are needed for `whenNew` to work
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Foo.class })
public class FooTest {
// Class Under Test
Foo cut;
@Mock
Bar barMock;
@Before
public void setUp() throws Exception {
cut = new Foo();
}
@After
public void tearDown() {
cut = null;
}
@Test
public void testFoo() throws Exception {
// Setup
PowerMockito.whenNew(Bar.class).withNoArguments()
.thenReturn(this.barMock);
// Test
cut.foo();
// Validations
Mockito.verify(this.barMock, Mockito.times(1)).someMethod();
}
}
Ich denke, Mockito @InjectMocks
ist der Weg zu gehen.
Abhängig von Ihrer Absicht können Sie verwenden:
Weitere Infos in docs
Nachfolgend ein Beispiel mit Feldinjektion:
Klassen:
public class Foo
{
private Bar bar = new Bar();
public void foo()
{
bar.someMethod();
}
}
public class Bar
{
public void someMethod()
{
//something
}
}
Prüfung:
@RunWith(MockitoJUnitRunner.class)
public class FooTest
{
@Mock
Bar bar;
@InjectMocks
Foo foo;
@Test
public void FooTest()
{
doNothing().when( bar ).someMethod();
foo.foo();
verify(bar, times(1)).someMethod();
}
}
Ja, wenn Sie wirklich wollen/müssen, können Sie PowerMock verwenden. Dies sollte als letzter Ausweg betrachtet werden. Mit PowerMock können Sie veranlassen, dass ein Aufruf aus dem Aufruf an den Konstruktor zurückgegeben wird. Dann überprüfen Sie den Spott. Das heißt, Csturtz ist die "richtige" Antwort.
Hier ist der Link zu Scheinkonstruktion neuer Objekte
Eine andere einfache Möglichkeit wäre, dem bar.someMethod () eine Protokollanweisung hinzuzufügen und dann festzustellen, dass Sie die besagte Nachricht sehen können, wenn Ihr Test ausgeführt wurde. Siehe Beispiele hier: Wie macht man eine JUnit-Bestätigung für eine Nachricht in einem Logger
Das ist besonders praktisch, wenn Ihre Bar.someMethod () private
ist.