我有一堂课如下:
public class A {
public A(String test) {
bla bla bla
}
public String check() {
bla bla bla
}
}
构造函数中的逻辑
A(String test)
和check()
是我试图模拟的东西。我想要任何类似的调用: new A($$$any string$$$).check()
返回一个虚拟字符串 "test"
。
我尝试过:
A a = mock(A.class);
when(a.check()).thenReturn("test");
String test = a.check(); // to this point, everything works. test shows as "tests"
whenNew(A.class).withArguments(Matchers.anyString()).thenReturn(rk);
// also tried:
//whenNew(A.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(rk);
new A("random string").check(); // this doesn't work
但似乎不起作用。
new A($$$any string$$$).check()
仍在执行构造函数逻辑,而不是获取 A
的模拟对象。
您发布的代码适用于我最新版本的 Mockito 和 Powermockito。也许你还没有准备好A? 试试这个:
A.java
public class A {
private final String test;
public A(String test) {
this.test = test;
}
public String check() {
return "checked " + this.test;
}
}
MockA.java
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class MockA {
@Test
public void test_not_mocked() throws Throwable {
assertThat(new A("random string").check(), equalTo("checked random string"));
}
@Test
public void test_mocked() throws Throwable {
A a = mock(A.class);
when(a.check()).thenReturn("test");
PowerMockito.whenNew(A.class).withArguments(Mockito.anyString()).thenReturn(a);
assertThat(new A("random string").check(), equalTo("test"));
}
}
这两个测试都应该通过mockito 1.9.0、powermockito 1.4.12 和 junit 4.8.2
据我所知,你不能用mockito来模拟构造函数,只能用方法来模拟。但根据 Mockito 谷歌代码页上的 wiki,有一种方法可以通过在类中创建一个返回该类的新实例的方法来模拟构造函数的行为。然后你可以模拟出该方法。以下是直接摘自 Mockito wiki 的摘录:
模式 1 - 使用单行方法创建对象
要使用模式 1(测试名为 MyClass 的类),您需要替换类似的调用
Foo foo = new Foo( a, b, c );
与
Foo foo = makeFoo( a, b, c );
并编写一个一行方法
Foo makeFoo( A a, B b, C c ) { return new Foo( a, b, c ); }
重要的是不要在方法中包含任何逻辑;只是创建的一行 目的。这样做的原因是该方法本身永远不会 进行单元测试。
当你来测试类时,你测试的对象将会 实际上是一个 Mockito 间谍,重写此方法,返回一个 嘲笑。因此,您正在测试的不是类本身,而是 它的稍微修改过的版本。
您的测试类可能包含类似的成员
@Mock private Foo mockFoo; private MyClass toTest = spy(new MyClass());
最后,在您的测试方法中,您模拟了对 makeFoo 带有类似
的行doReturn( mockFoo ) .when( toTest ) .makeFoo( any( A.class ), any( B.class ), any( C.class ));
如果您愿意,您可以使用比any()更具体的匹配器 检查传递给构造函数的参数。
如果您只是想返回您的类的模拟对象,我认为这应该适合您。无论如何,您可以在这里阅读有关模拟对象创建的更多信息:
使用 Mockito,您可以使用
withSettings()
。例如,如果 CounterService
需要 2 个依赖项,您可以将它们作为模拟传递:
UserService userService = Mockito.mock(UserService.class);
SearchService searchService = Mockito.mock(SearchService.class);
CounterService counterService = Mockito.mock(CounterService.class, withSettings().useConstructor(userService, searchService));
不使用 Powermock .... 请参阅下面基于 Ben Glasser 答案的示例,因为我花了一些时间才弄清楚 .. 希望可以节省一些时间 ...
原班:
public class AClazz {
public void updateObject(CClazz cClazzObj) {
log.debug("Bundler set.");
cClazzObj.setBundler(new BClazz(cClazzObj, 10));
}
}
修改后的类:
@Slf4j
public class AClazz {
public void updateObject(CClazz cClazzObj) {
log.debug("Bundler set.");
cClazzObj.setBundler(getBObject(cClazzObj, 10));
}
protected BClazz getBObject(CClazz cClazzObj, int i) {
return new BClazz(cClazzObj, 10);
}
}
测试班
public class AClazzTest {
@InjectMocks
@Spy
private AClazz aClazzObj;
@Mock
private CClazz cClazzObj;
@Mock
private BClazz bClassObj;
@Before
public void setUp() throws Exception {
Mockito.doReturn(bClassObj)
.when(aClazzObj)
.getBObject(Mockito.eq(cClazzObj), Mockito.anyInt());
}
@Test
public void testConfigStrategy() {
aClazzObj.updateObject(cClazzObj);
Mockito.verify(cClazzObj, Mockito.times(1)).setBundler(bClassObj);
}
}
Mockito 在测试最终方法、静态方法和私有方法时存在局限性。
替代解决方案: 使用 jMockit 测试库,您可以非常简单直接地做一些事情,如下所示:
java.io.File 类的模拟构造函数:
new MockUp<File>(){
@Mock
public void $init(String pathname){
System.out.println(pathname);
// or do whatever you want
}
};
模拟静态方法:
使用 Mockito 4(但我怀疑 3.5.0 版本中的 Mockito 也是如此),您可以模拟构造函数,并且在初始化程序中,您可以断言参数的值。
例如:
try (MockedConstruction<A> constr = mockConstruction(A.class,
(mock, context) -> {
if (context.getCount() == 1) {
assertArrayEquals(context.arguments().toArray(), new Object[] {"test"});
} else {
fail("No more calls should happen");
}
})) {
// Do the rest of assertions.
}
请注意,您需要将 MockedConstruction 实例化放入 try-with-resources 中,否则模拟的构造会泄漏到测试之外。