我想测试一个抽象类。当然,我可以手动编写一个从该类继承的模拟。
我可以使用mock框架(我使用Mockito)而不是手工制作我的mock吗?如何?
我想测试一个抽象类。当然,我可以手动编写一个从该类继承的模拟。
我可以使用mock框架(我使用Mockito)而不是手工制作我的mock吗?如何?
当前回答
真正让我对模拟抽象类感到糟糕的是,既没有调用默认构造函数YourAbstractClass()(在mock中缺少super()),也似乎没有任何方法在Mockito中默认初始化模拟属性(例如List属性与空ArrayList或LinkedList)。
我的抽象类(基本上生成了类源代码)没有为列表元素提供依赖setter注入,也没有初始化列表元素的构造函数(我试图手动添加)。
只有类属性使用默认初始化:
private List<MyGenType> dep1 = new ArrayList<MyGenType>();
private List<MyGenType> dep2 = new ArrayList<MyGenType>();
因此,如果不使用真正的对象实现(例如单元测试类中的内部类定义,覆盖抽象方法)和监视真正的对象(它会进行适当的字段初始化),就无法模拟抽象类。
遗憾的是,只有PowerMock才能进一步提供帮助。
其他回答
假设你的测试类和你的测试类在同一个包中(在不同的源根下),你可以简单地创建mock:
YourClass yourObject = mock(YourClass.class);
并调用您想要测试的方法,就像调用其他方法一样。
你需要为每个被调用的方法提供期望,以及任何具体方法调用超方法的期望——不确定如何用Mockito做到这一点,但我相信这在EasyMock中是可能的。
所有这些操作都是创建YouClass的具体实例,从而节省了为每个抽象方法提供空实现的工作量。
顺便说一句,我经常发现在我的测试中实现抽象类很有用,它作为一个示例实现,我通过它的公共接口进行测试,尽管这确实依赖于抽象类提供的功能。
模拟框架的设计目的是为了更容易模拟出正在测试的类的依赖关系。当您使用模拟框架模拟一个类时,大多数框架都会动态地创建一个子类,并将方法实现替换为用于检测方法何时被调用并返回假值的代码。
在测试抽象类时,您希望执行Subject Under Test (SUT)的非抽象方法,因此模拟框架并不是您想要的。
造成这种困惑的部分原因是,您所链接的问题的答案要求手工创建一个从抽象类扩展而来的模拟。我不认为这样的课程是mock。mock是一个类,它被用作依赖项的替代品,它是按照期望进行编程的,并且可以查询这些期望是否得到满足。
相反,我建议在测试中定义抽象类的非抽象子类。如果这导致了太多的代码,那么这可能表明您的类难以扩展。
另一种解决方案是使您的测试用例本身是抽象的,使用抽象的方法来创建SUT(换句话说,测试用例将使用模板方法设计模式)。
class Dependency{
public void method(){};
}
public abstract class My {
private Dependency dependency;
public abstract boolean myAbstractMethod();
public void myNonAbstractMethod() {
// ...
dependency.method();
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyTest {
@InjectMocks
private My my = Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS);
// we can mock dependencies also here
@Mock
private Dependency dependency;
@Test
private void shouldPass() {
// can be mock the dependency object here.
// It will be useful to test non abstract method
my.myNonAbstractMethod();
}
}
真正让我对模拟抽象类感到糟糕的是,既没有调用默认构造函数YourAbstractClass()(在mock中缺少super()),也似乎没有任何方法在Mockito中默认初始化模拟属性(例如List属性与空ArrayList或LinkedList)。
我的抽象类(基本上生成了类源代码)没有为列表元素提供依赖setter注入,也没有初始化列表元素的构造函数(我试图手动添加)。
只有类属性使用默认初始化:
private List<MyGenType> dep1 = new ArrayList<MyGenType>();
private List<MyGenType> dep2 = new ArrayList<MyGenType>();
因此,如果不使用真正的对象实现(例如单元测试类中的内部类定义,覆盖抽象方法)和监视真正的对象(它会进行适当的字段初始化),就无法模拟抽象类。
遗憾的是,只有PowerMock才能进一步提供帮助。
您可以实例化一个匿名类,注入您的模拟,然后测试该类。
@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的可见性。