我想测试一个抽象类。当然,我可以手动编写一个从该类继承的模拟。
我可以使用mock框架(我使用Mockito)而不是手工制作我的mock吗?如何?
我想测试一个抽象类。当然,我可以手动编写一个从该类继承的模拟。
我可以使用mock框架(我使用Mockito)而不是手工制作我的mock吗?如何?
当前回答
您可以在测试中使用匿名类扩展抽象类。 例如(使用Junit 4):
private AbstractClassName classToTest;
@Before
public void preTestSetup()
{
classToTest = new AbstractClassName() { };
}
// Test the AbstractClassName methods.
其他回答
PowerMock的Whitebox.invokeMethod(..)在这种情况下很方便。
您可以通过使用间谍来实现这一点(尽管使用Mockito 1.8+的最新版本)。
public abstract class MyAbstract {
public String concrete() {
return abstractMethod();
}
public abstract String abstractMethod();
}
public class MyAbstractImpl extends MyAbstract {
public String abstractMethod() {
return null;
}
}
// your test code below
MyAbstractImpl abstractImpl = spy(new MyAbstractImpl());
doReturn("Blah").when(abstractImpl).abstractMethod();
assertTrue("Blah".equals(abstractImpl.concrete()));
尝试使用自定义答案。
例如:
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class CustomAnswer implements Answer<Object> {
public Object answer(InvocationOnMock invocation) throws Throwable {
Answer<Object> answer = null;
if (isAbstract(invocation.getMethod().getModifiers())) {
answer = Mockito.RETURNS_DEFAULTS;
} else {
answer = Mockito.CALLS_REAL_METHODS;
}
return answer.answer(invocation);
}
}
它将为抽象方法返回mock,为具体方法调用real方法。
您可以实例化一个匿名类,注入您的模拟,然后测试该类。
@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是子类,只是部分Mock。
使用Mockito.mock(My.class, Answers.CALLS_REAL_METHODS),然后模拟被调用的任何抽象方法。
例子:
public abstract class My {
public Result methodUnderTest() { ... }
protected abstract void methodIDontCareAbout();
}
public class MyTest {
@Test
public void shouldFailOnNullIdentifiers() {
My my = Mockito.mock(My.class, Answers.CALLS_REAL_METHODS);
Assert.assertSomething(my.methodUnderTest());
}
}
注意:这个解决方案的美妙之处在于您不必实现抽象方法。CALLS_REAL_METHODS导致所有真实方法按原样运行,只要您不在测试中存根它们。
在我看来,这比使用间谍更简洁,因为间谍需要一个实例,这意味着您必须为抽象类创建一个可实例化的子类。