我想测试一个抽象类。当然,我可以手动编写一个从该类继承的模拟。
我可以使用mock框架(我使用Mockito)而不是手工制作我的mock吗?如何?
我想测试一个抽象类。当然,我可以手动编写一个从该类继承的模拟。
我可以使用mock框架(我使用Mockito)而不是手工制作我的mock吗?如何?
当前回答
PowerMock的Whitebox.invokeMethod(..)在这种情况下很方便。
其他回答
如果你只是需要测试一些具体方法而不涉及任何抽象,你可以使用CALLS_REAL_METHODS(见Morten的回答),但是如果被测试的具体方法调用一些抽象,或未实现的接口方法,这将不起作用——Mockito将抱怨“不能在java接口上调用真实方法”。
(是的,这是一个糟糕的设计,但有些框架,例如Tapestry 4,会强迫你这样做。)
解决方法是反转这种方法——使用普通的模拟行为(即,所有东西都被模拟/存根)并使用doCallRealMethod()显式地调用测试中的具体方法。如。
public abstract class MyClass {
@SomeDependencyInjectionOrSomething
public abstract MyDependency getDependency();
public void myMethod() {
MyDependency dep = getDependency();
dep.doSomething();
}
}
public class MyClassTest {
@Test
public void myMethodDoesSomethingWithDependency() {
MyDependency theDependency = mock(MyDependency.class);
MyClass myInstance = mock(MyClass.class);
// can't do this with CALLS_REAL_METHODS
when(myInstance.getDependency()).thenReturn(theDependency);
doCallRealMethod().when(myInstance).myMethod();
myInstance.myMethod();
verify(theDependency, times(1)).doSomething();
}
}
更新如下:
对于非void方法,你需要使用encallrealmethod()来代替,例如:
when(myInstance.myNonVoidMethod(someArgument)).thenCallRealMethod();
否则Mockito将报怨“检测到未完成的存根”。
PowerMock的Whitebox.invokeMethod(..)在这种情况下很方便。
Mockito允许通过@Mock注释来模拟抽象类:
public abstract class My {
public abstract boolean myAbstractMethod();
public void myNonAbstractMethod() {
// ...
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyTest {
@Mock(answer = Answers.CALLS_REAL_METHODS)
private My my;
@Test
private void shouldPass() {
BDDMockito.given(my.myAbstractMethod()).willReturn(true);
my.myNonAbstractMethod();
// ...
}
}
缺点是如果需要构造函数参数,则不能使用它。
您可以实例化一个匿名类,注入您的模拟,然后测试该类。
@RunWith(MockitoJUnitRunner.class)
public class ClassUnderTest_Test {
private ClassUnderTest classUnderTest;
@Mock
MyDependencyService myDependencyService;
@Before
public void setUp() throws Exception {
this.classUnderTest = getInstance();
}
private ClassUnderTest getInstance() {
return new ClassUnderTest() {
private ClassUnderTest init(
MyDependencyService myDependencyService
) {
this.myDependencyService = myDependencyService;
return this;
}
@Override
protected void myMethodToTest() {
return super.myMethodToTest();
}
}.init(myDependencyService);
}
}
请记住,必须保护抽象类ClassUnderTest的属性myDependencyService的可见性。
假设你的测试类和你的测试类在同一个包中(在不同的源根下),你可以简单地创建mock:
YourClass yourObject = mock(YourClass.class);
并调用您想要测试的方法,就像调用其他方法一样。
你需要为每个被调用的方法提供期望,以及任何具体方法调用超方法的期望——不确定如何用Mockito做到这一点,但我相信这在EasyMock中是可能的。
所有这些操作都是创建YouClass的具体实例,从而节省了为每个抽象方法提供空实现的工作量。
顺便说一句,我经常发现在我的测试中实现抽象类很有用,它作为一个示例实现,我通过它的公共接口进行测试,尽管这确实依赖于抽象类提供的功能。