我是Mockito的新手。
给定下面的类,如何someMethod
在调用后使用Mockito验证一次foo
被调用的对象呢?
public class Foo
{
public void foo(){
Bar bar = new Bar();
bar.someMethod();
}
}
我想打以下电话,
verify(bar, times(1)).someMethod();
bar
的模拟实例在哪里Bar
?
我是Mockito的新手。
给定下面的类,如何someMethod
在调用后使用Mockito验证一次foo
被调用的对象呢?
public class Foo
{
public void foo(){
Bar bar = new Bar();
bar.someMethod();
}
}
我想打以下电话,
verify(bar, times(1)).someMethod();
bar
的模拟实例在哪里Bar
?
Answers:
如果注入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如何驱动代码设计的一个示例。
Bar bar = mock(Bar.class)
而不是Bar bar = new Bar();
BarFactory myFactory = mock(BarFactory.class); when(myFactory.createBar()).thenReturn(bar);
最经典的回答是:“您不这样做”。您测试的公共API Foo
,而不是其内部。
是否存在受该Foo
对象影响的任何行为(或者,环境中的其他不良对象)foo()
?如果是这样,请进行测试。如果不是,该方法有什么作用?
Foo
是public void foo()
,其中内部仅与酒吧相关。
verify()
,即使您不再在集成测试的圣坛上崇拜,Mockito 的确也变得非常有帮助。
如果您不想使用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对象。
希望能帮助到你。
使用示例代码的解决方案 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();
}
}
我认为Mockito @InjectMocks
是要走的路。
根据您的意图,您可以使用:
文档中的更多信息
下面是一个使用字段注入的示例:
类:
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();
}
}
另一种简单的方法是在bar.someMethod()中添加一些日志语句,然后确定执行测试时可以看到上述消息,请参见此处的示例:如何在记录器中对消息执行JUnit断言
当您的Bar.someMethod()为时,这尤其方便private
。