是否可以在不嘲笑或调用其构造函数的情况下使用复杂的构造函数实例化类?这很有用,因为单位测试类不需要每次添加新的依赖项时都需要更改。
嘲笑服务通过创建类的新实现,用空的方法覆盖来解决问题,但这不是实现的实例。这是一个问题,因为任何时候称为模拟服务中的方法时,必须告诉Mockito调用真实方法。相反,该服务的实际实施将是首选。
例如:
class ComplexService {
private Service service1;
private Service service2;
private Service service3;
private Service service4;
private Service service5;
ComplexConstructor(Service service1, Service service2, Service service3, Service service4, Service service5) {
this.service1 = service1;
this.service2 = service2;
this.service3 = service3;
this.service4 = service4;
this.service5 = service5;
}
boolean methodToTest() {
return service1.get();
}
}
在单元测试类中,是否可以实例化实施实现,而无需调用其构造函数?
public class ComplexConostructorTest {
private ComplexConstructor complexConstructor;
private Service serviceMock;
@Before
public void init() {
/*
Somehow create implementation of complexConstructor
without calling constructor
. . .
*/
// Mock dependency
ReflectionTestUtils.setField(complexConstructor,
"service1",
serviceMock = Mockito.mock(Service.class));
}
@Test
public void service1Test() {
when(serviceMock.get())
.thenReturn(true);
assertTrue(complexConstructor.methodToTest());
}
}
编辑
有可能使用反射,我希望在Junit或Mockito中有一个内置的方式来实现同一件事。这是使用反射的方法。
据我所知,@Before
public void init() {
Constructor<?> constructor = ComplexConstructor.class.getConstructors()[0];
complexConstructor = (ComplexConstructor) constructor.newInstance(new Object[constructor.getParameterCount()]);
// Mock dependency
ReflectionTestUtils.setField(complexConstructor,
"service1",
serviceMock = Mockito.mock(Service.class));
}
这是不可能的,但是您可以简单地在类中添加一个更简单的构造函数并将其用于测试。另一方面,如果测试测试与应用程序中的对象测试对象,则我不确定这种测试的效果如何。
您可以,但您可能不想:
ComplexConstructor partialMock =
Mockito.mock(ComplexConstructor.class, CALLS_REAL_METHODS);
这个"部分模拟"实例将没有调用其构造函数或字段初始化器,但是对正在测试的系统进行的所有调用都将调用班级的真实行为。(从技术上讲,该类还将其equals
和hashCode
覆盖为摩索哥的目的,并且该类将是综合构造者的生成子类,而不是复杂的构造者本身。)
以这种方式,您可以与构造函数隔离,但是由于您抑制了按测试的班级行为的任意子集,因此很难确切确切地确定您正在测试的内容了,由于测试通过了,因此系统可以自信。这应该是您测试的主要目标,并且可能很难通过部分模拟实现这一目标。同事或合作者可以正确地观察到您不应该出于这个原因嘲笑您正在测试的系统。
尽管我个人认为需要更改单元测试以在需要时提供模拟,但您可以创建与测试分开的工厂,以提供鸡舍构造者的测试实例,或者考虑使用依赖关系自动向您的系统提供模拟的注入框架。
看起来您正在混合几个术语和概念。让我帮助您更好地了解它们。
这很有用,因为单位测试类不需要每次添加新的依赖项时都需要更改。
您的班级有许多通过构造函数提供的依赖项。如果您正在编写单元测试,您的目标是用于测试此依赖类,应模拟所有依赖性。这就是为什么它被称为单位测试。这意味着,对于班级的每一个新依赖性,应通过添加新模拟及其模拟行为来更新测试。
必须告知摩索哥称呼真实方法。相反,该服务的实际实施将是首选。
考虑集成测试,在这种情况下,您只能嘲笑一些依赖项,而其他依赖项当然可以按预期或"真实"工作,直到当然嘲笑它们为止。但是,如果您只想避免支持测试,那不是正确的方法。
请不要尝试通过反射从测试中破解您的测试课程。这可能会导致错误的测试结果,浪费时间和整体失望:)嘲笑PowerMock和Jmockit等库提供任何类型的黑客,例如您试图实施自己的插件,并且一般而言。
强大的责任是
您可以创建辅助方法作为测试代码的一部分,例如某些具有构造对象的描述性名称的工厂方法。例如,make_default_ComplexService
等等,按照您的测试需要。然后,测试可以使用这些方法,如果构造函数更改,则在许多情况下,您只需要更新辅助方法而不是所有测试。这种方法足够通用,还可以将您的测试与剧烈的更改分开,例如将"带有参数的构造函数"方法转入"具有很多设定器的非argument构造函数"方法。
这种方法将减少测试的维护工作,您仍将使用原始的构造函数(因为工厂称呼它)而不是某些假构造函数,并且您的测试代码甚至可能比直接构造函数的调用更可读如果出厂方法的名称得到了很好的选择。
您可以在您的类中添加一个无参数构造函数,并为所有字段创建setter。
class ComplexService {
private Service service1;
private Service service2;
private Service service3;
private Service service4;
private Service service5;
public ComplexService(){
super();
}
...
public void setService1(Service service1){
this.service1 = service1;
}
//for other fields too
...
}
在您的测试中,您将其称为:
myService = new ComplexService()
myService.setService1(service1);
...