我是 Mockito 的新手。
给定下面的类,我如何使用 Mockito 来验证 someMethod
在 foo
被调用后被调用了一次?
public class Foo
{
public void foo(){
Bar bar = new Bar();
bar.someMethod();
}
}
我想拨打以下验证电话,
verify(bar, times(1)).someMethod();
其中 bar
是 Bar
的模拟实例。
如果您注入 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
对象(或者,不太好,环境中的某些其他对象)的任何行为?如果是这样,请测试一下。如果不是,该方法有什么作用?
Foo
的公共 API 是 public void foo()
,其中内部仅条相关。
verify()
确实变得非常有帮助,即使您不再崇拜集成测试的圣坛。
如果您不想使用 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 对象。
希望能帮助到你。
我认为 Mockito @InjectMocks
是要走的路。
根据您的意图,您可以使用:
构造函数注入 属性设置器注入 字段注入
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();
}
}
使用 PowerMockito.whenNew
的示例代码解决方案
模拟全部 1.10.8
powermock 核心 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();
}
}
https://i.stack.imgur.com/gHdue.png
是的,如果你真的想要/需要这样做,你可以使用 PowerMock。这应该被视为最后的手段。使用 PowerMock,您可以使它从对构造函数的调用中返回一个模拟。然后在模拟上进行验证。也就是说,csturtz 是“正确”的答案。
这是 Mock construction of new objects 的链接
另一种简单的方法是向 bar.someMethod() 添加一些日志语句,然后确定您可以在执行测试时看到上述消息,请参见此处的示例:How to do a JUnit assert on a message in a logger
当您的 Bar.someMethod() 为 private
时,这尤其方便。
我今天遇到了这个问题,我不想使用 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 bar = mock(Bar.class)
而不是Bar bar = new Bar();
BarFactory myFactory = mock(BarFactory.class); when(myFactory.createBar()).thenReturn(bar);