509 Stimmen

Mockito: Wie kann man überprüfen, ob die Methode auf einem Objekt aufgerufen wurde, das innerhalb einer Methode erstellt wurde?

Ich bin neu bei Mockito.

Wie kann ich Mockito verwenden, um zu überprüfen, dass die Klasse someMethod wurde genau einmal aufgerufen, nachdem foo herangezogen wurde?

public class Foo
{
    public void foo(){
        Bar bar = new Bar();
        bar.someMethod();
    }
}

Ich möchte den folgenden Kontrollanruf tätigen,

verify(bar, times(1)).someMethod();

donde bar ist eine nachgebildete Instanz von Bar .

524voto

csturtz Punkte 6012

Injektion von Abhängigkeiten

Wenn Sie die Bar-Instanz oder eine Fabrik, die für die Erstellung der Bar-Instanz verwendet wird, injizieren (oder eine der anderen 483 Möglichkeiten, dies zu tun), haben Sie den notwendigen Zugriff, um den Test durchzuführen.

Beispiel aus dem Werk:

Gegeben eine Foo-Klasse, die so geschrieben ist:

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 wie folgt einfügen:

@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 dafür, wie TDD (Test Driven Development) das Design Ihres Codes beeinflussen kann.

26voto

siulkilulki Punkte 896

Ich glaube, Mockito @InjectMocks ist der richtige Weg.

Je nach Ihrer Absicht können Sie diese verwenden:

  1. Injektion des Konstruktors
  2. Injektion von Eigenschaftssetzern
  3. Feldinjektion

Mehr Infos unter docs

Nachfolgend finden Sie 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
    }
}

Test:

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

22voto

Michael Brewer-Davis Punkte 13669

Die klassische Antwort lautet: "Nein, das tun Sie nicht. Sie testen die öffentliche API von Foo und nicht sein Innenleben.

Gibt es ein Verhalten des Foo Objekt (oder, weniger gut, ein anderes Objekt in der Umgebung), das von foo() ? Wenn ja, testen Sie das. Und wenn nicht, was macht die Methode?

19voto

raspacorp Punkte 4503

Wenn Sie keine DI oder Fabriken verwenden möchten. Sie können Ihre Klasse auf etwas trickreiche Weise umstrukturieren:

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 Ihre 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, dies so tun:

public class thirdClass {

   public void someOtherMethod() {
      Foo myFoo = new Foo();
      myFoo.foo(null);
   }
}

Wie Sie sehen können, wenn Sie die Methode auf diese Weise aufrufen, müssen Sie die Bar-Klasse in keiner anderen Klasse importieren, die Ihre foo-Methode aufruft, was vielleicht etwas ist, das Sie wollen.

Der Nachteil ist natürlich, dass Sie dem Aufrufer erlauben, das Bar Object zu setzen.

Ich hoffe, es hilft.

11voto

javaPlease42 Punkte 4183

Lösung für Ihren Beispielcode mit PowerMockito.whenNew

  • mockito-all 1.10.8
  • powermock-core 1.6.1
  • powermock-module-junit4 1.6.1
  • powermock-api-mockito 1.6.1
  • junit 4.12

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

    }

}

JUnit-Ausgabe JUnit Output

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X