簡體   English   中英

Mockito:如何驗證在方法中創建的 object 上調用了方法?

[英]Mockito : how to verify method was called on an object created within a method?

我是 Mockito 的新用戶。

鑒於下面的 class,我如何使用 Mockito 來驗證someMethod在調用foo之后恰好被調用了一次?

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

我想撥打以下驗證電話,

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

其中barBar的模擬實例。

依賴注入

如果您注入Bar實例或用於創建Bar實例的工廠(或其他483種方法之一),您將擁有執行測試所需的訪問權限。

工廠示例:

給定一個這樣編寫的Foo類:

public class Foo {
  private BarFactory barFactory;

  public Foo(BarFactory factory) {
    this.barFactory = factory;
  }

  public void foo() {
    Bar bar = this.barFactory.createBar();
    bar.someMethod();
  }
}

在你的測試方法中,你可以像這樣注入一個BarFactory:

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

額外獎勵:這是TDD如何推動代碼設計的一個例子。

經典的回答是,“你沒有。” 你測試Foo的公共API,而不是它的內部。

是否存在受foo()影響的Foo對象(或不太好,環境中的其他對象)的任何行為? 如果是這樣,請測試一下。 如果沒有,該方法有何作用?

如果您不想使用DI或工廠。 您可以通過一些棘手的方式重構您的課程:

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
    }
}

而你的測試班:

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

那么調用你的foo方法的類就會這樣做:

public class thirdClass {

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

正如您在以這種方式調用方法時所看到的那樣,您不需要在調用您的foo方法的任何其他類中導入Bar類,這可能是您想要的。

當然缺點是你允許調用者設置Bar Object。

希望能幫助到你。

使用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輸出 JUnit輸出

我認為Mockito @InjectMocks是要走的路。

根據您的意圖,您可以使用:

  1. 構造函數注入
  2. 物業二傳手注射
  3. 現場注射

更多信息在docs中

以下是現場注入的示例:

類別:

public class Foo
{
    private Bar bar = new Bar();

    public void foo() 
    {
        bar.someMethod();
    }
}

public class Bar
{
    public void someMethod()
    {
         //something
    }
}

測試:

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

是的,如果您真的想要/需要這樣做,您可以使用PowerMock。 這應該被視為最后的手段。 使用PowerMock,您可以使它從調用返回模擬構造函數。 然后在模擬上進行驗證。 也就是說,csturtz是“正確”的答案。

這是Mock構造新對象的鏈接

我今天遇到了這個問題,我不想使用 PowerMock 或其他東西。 我只是想做一個測試來確保某個方法被調用。 我找到了這篇文章,發現沒有人提到過這種方法。

在不添加更多依賴項或類似內容的情況下實現此目的的一種方法技術含量很低,但它有效:

@Test
public void testSomeMethodIsCalledOnce() throws Exception {
    final AtomicInteger counter = new AtomicInteger(0);
    Mockito.when(someObject.theMethodIWant(anyString()))
        .then((Answer<ReturnValue>) __ -> {
            teller.incrementAndGet();
            return theExpectedAnswer;
        });
    theObjectUnderTest.theMethod(someTestValue);

    assertEquals(1, teller.get());
}

這非常簡單,而且很容易看出發生了什么。 當我想要的方法被調用時(它在這里被嘲笑),做這些事情。 其中包括對 AtomicInteger 的 incrementAndGet 調用。 可以在此處使用 int[],但我認為這不是很清楚。 我們只是使用最終的東西,我們可以增加它。 這是我們使用的 lambda 的限制。

它有點粗糙,但它以簡單明了的方式完成了工作。 至少如果你知道你的 lambdas 和 Mockito。

另一個簡單的方法是將一些日志語句添加到bar.someMethod(),然后確定您可以在執行測試時看到所述消息,請參閱此處的示例: 如何在記錄器中對消息執行JUnit斷言

當您的Bar.someMethod()是private時,這尤其方便。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM