Java Mockito:如何验证在方法中创建的对象上调用了方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9841623/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 08:27:08  来源:igfitidea点击:

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

javaunit-testingjunitmockito

提问by mre

I am new to Mockito.

我是 Mockito 的新手。

Given the class below, how can I use Mockito to verify that someMethodwas invoked exactly once after foowas invoked?

鉴于下面的类,我如何使用 Mockito 来验证在someMethod被调用后恰好被调用一次foo

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

I would like to make the following verification call,

我想拨打以下验证电话,

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

where baris a mocked instance of Bar.

bar的模拟实例在哪里Bar

采纳答案by csturtz

Dependency Injection

依赖注入

If you inject the Bar instance, or a factory that is used for creating the Bar instance (or one of the other 483 ways of doing this), you'd have the access necessary to do perform the test.

如果您注入 Bar 实例或用于创建 Bar 实例的工厂(或执行此操作的其他 483 种方法之一),您将拥有执行测试所需的访问权限。

Factory Example:

工厂示例:

Given a Foo class written like this:

给定一个像这样写的 Foo 类:

public class Foo {
  private BarFactory barFactory;

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

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

in your test method you can inject a BarFactory like this:

在您的测试方法中,您可以像这样注入 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();
}

Bonus: This is an example of how TDD can drive the design of your code.

奖励:这是 TDD 如何驱动代码设计的示例。

回答by Michael Brewer-Davis

The classic response is, "You don't." You test the public API of Foo, not its internals.

经典的回答是,“你没有。” 您测试的是 的公共 API Foo,而不是其内部。

Is there any behavior of the Fooobject (or, less good, some other object in the environment) that is affected by foo()? If so, test that. And if not, what does the method do?

是否存在受 影响的Foo对象(或环境中的其他对象)的任何行为foo()?如果是这样,测试一下。如果没有,该方法有什么作用?

回答by John B

Yes, if you really want / need to do it you can use PowerMock. This should be considered a last resort. With PowerMock you can cause it to return a mock from the call to the constructor. Then do the verify on the mock. That said, csturtz's is the "right" answer.

是的,如果您真的想要/需要这样做,您可以使用 PowerMock。这应该被视为最后的手段。使用 PowerMock,您可以使其从对构造函数的调用返回模拟。然后在模拟上进行验证。也就是说,csturtz 是“正确”的答案。

Here is the link to Mock construction of new objects

这是新对象的模拟构造的链接

回答by raspacorp

If you don't want to use DI or Factories. You can refactor your class in a little tricky way:

如果您不想使用 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
    }
}

And your test class:

还有你的测试课:

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

Then the class that is calling your foo method will do it like this:

然后调用你的 foo 方法的类会这样做:

public class thirdClass {

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

As you can see when calling the method this way, you don't need to import the Bar class in any other class that is calling your foo method which is maybe something you want.

正如您在以这种方式调用方法时所看到的那样,您不需要在调用 foo 方法的任何其他类中导入 Bar 类,这可能是您想要的。

Of course the downside is that you are allowing the caller to set the Bar Object.

当然,缺点是您允许调用者设置 Bar 对象。

Hope it helps.

希望能帮助到你。

回答by javaPlease42

Solution for your example code using PowerMockito.whenNew

使用示例代码的解决方案 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
  • mockito-all 1.10.8
  • powermock 核心 1.6.1
  • powermock-module-junit4 1.6.1
  • powermock-api-mockito 1.6.1
  • 联合 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 OutputJUnit Output

JUnit 输出JUnit 输出

回答by siulkilulki

I think Mockito @InjectMocksis the way to go.

我认为 Mockito@InjectMocks是要走的路。

Depending on your intention you can use:

根据您的意图,您可以使用:

  1. Constructor injection
  2. Property setter injection
  3. Field injection
  1. 构造函数注入
  2. 属性设置器注入
  3. 现场注入

More info in docs

文档中的更多信息

Below is an example with field injection:

下面是一个带有字段注入的示例:

Classes:

课程:

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

回答by Nestor Milyaev

Another simple way would be add some log statement to the bar.someMethod() and then ascertain you can see the said message when your test executed, see examples here: How to do a JUnit assert on a message in a logger

另一种简单的方法是向 bar.someMethod() 添加一些日志语句,然后确定您在测试执行时可以看到所述消息,请参见此处的示例:如何对记录器中的消息执行 JUnit 断言

That is especially handy when your Bar.someMethod() is private.

当您的 Bar.someMethod() 是private.