我有以下代码:
public class MyClass() {
private MyObject myObject = getMyObject();
public MyObject getMyObject() {
if (myObject == null) {
myObject = MyStaticClass.createMyObject();
}
return myObject;
}
// heaps more methods
}
public class MyTest() {
private MyClass spyMyClass = spy(new MyClass());
public MyTest() {
doReturn(null).when(spyMyClass).getMyObject();
}
@Test
public void someTest() {
ClassUnderTest c = new ClassUnderTest();
assertTrue(c.someMethod(spyMyClass));
}
}
测试失败,错误Could not initialise class MyStaticClass
。原因是这个静态类正在初始化更多在测试期间不可用的其他类对象(例如数据库),我不在乎,因为您可以看到在调用方法getMyObject()
时返回 null。
但是这个意图也失败了,因为在到达doReturn(null)
行之前,测试已经在spy(new MyClass())
行失败,在该行中,它调用getMyObject()
来初始化私有成员myObject
。
上述情况的解决方法是使用mock(MyClass.class)
而不是spy(new MyClass())
,这样私有成员myObject
就不会被初始化,因此不会调用真正的getMyObject()
方法。
但是这种解决方法给我带来了另一个麻烦,因为这意味着我将不得不为MyClass
中的堆更多方法进行一些配置(甚至只是为了doCallRealMethod()
)。
问题:有没有另一种解决方案,我仍然可以在MyClass
实例上使用间谍,这样我就可以忘记在此类中配置这些堆更多的方法,但我可以绕过Could not initialise class MyStaticClass
错误?
附言我不能简单地使用 Power Mock 来模拟MyStaticClass
因为我已经在使用另一个测试运行器进行MyTest
。除非你的答案可以显示同时运行两个测试运行程序是多么容易,而无需实现将两者组合在一起的新混合测试运行程序。
感谢亚当,现在我有一个很好的工作代码:
public class MyTest() {
private MyClass spyMyClass = spy(new MyClass() {
@Override
public MyObject getMyObject() {
return null;
}
});
@Test
public void someTest() {
ClassUnderTest c = new ClassUnderTest();
assertTrue(c.someMethod(spyMyClass));
}
}
创建一个MyClass
的子类并在private MyClass spyMyClass = spy(new TestMyClass());
中使用它:
class TestMyClass extends MyClass {
@Override // fortunately, the original method called in constructor can be overridden (what could be considered bad)
public MyObject getMyObject() {
// something that does not fail the constructor
}
}
通常,这是问题的潜在原因,因为您在构造函数中调用非私有、非最终方法。对于测试用例,这可能是可以接受的。
稍微离开一点,我想看看这个MyClass
对象的责任是明智的。是不是做得太多了,因此很难测试和互动?这通常会导致"难以嘲笑"的综合症。