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

时间:2012-03-23 15:09:32

标签: java unit-testing junit mockito

我是Mockito的新手。

鉴于下面的课程,如何在调用someMethod后使用Mockito验证foo是否被调用了一次?

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

我想进行以下验证电话,

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

其中barBar的模拟实例。

7 个答案:

答案 0 :(得分:289)

Dependency Injection

如果您注入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();
}

Bonus:这是TDD如何推动代码设计的一个例子。

答案 1 :(得分:17)

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

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

答案 2 :(得分:12)

如果您不想使用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类,这可能是您想要的。

当然缺点是你允许调用者设置条形对象。

希望它有所帮助。

答案 3 :(得分:8)

使用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 Output

答案 4 :(得分:8)

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

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

  1. 构造函数注入
  2. 物业设定者注射
  3. 现场注射
  4. 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();
        }
    }
    

答案 5 :(得分:3)

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

以下是Mock construction of new objects

的链接

答案 6 :(得分:0)

另一种简单的方法是在bar.someMethod()中添加一些日志语句,然后确定执行测试时可以看到上述消息,请参见此处的示例:How to do a JUnit assert on a message in a logger

当Bar.someMethod()为private时,这特别方便。