我开发了一些行为类似的类,它们都实现了相同的接口。我实现了一个工厂,该工厂创建适当的对象并返回接口。我正在为工厂编写单元测试。您所获得的只是对象的接口。
测试工厂正常工作的最佳方法是什么?
我想知道Java的答案,但是如果有跨语言的解决方案,我想知道。
数字2,答案是否会像其他答案一样?如果是这样,我也将标记另一个接受的答案,并将我的问题改写为既解决了返回接口的工厂,又不知道实现该接口的具体类是什么类型的情况,以及您确实知道具体的类是什么的情况已使用。
您要尝试的不是单元测试
如果您测试返回的对象是否为特定具体类的实例,则不是单元测试。您正在进行集成测试。尽管集成测试很重要,但这不是一回事。
在单元测试中,您只需要测试对象本身。如果您对返回的抽象对象的具体类型进行断言,则您正在测试返回对象的实现。
一般对象的单元测试
在进行单元测试时,您需要声明四件事:
查询(非空方法)的返回值就是您期望的值。
命令(无效方法)的副作用会按您期望的那样修改对象本身。
接收发送到其他对象的命令(通常使用模拟完成)。
此外,您只想测试可以从对象实例(即公共接口)中观察到的内容。否则,您将自己束缚于一组特定的实施细节。这将要求您在这些详细信息更改时更改测试。
单元测试工厂
在工厂上进行单元测试真的没有意思,因为您对查询的返回对象的行为不感兴趣。该行为(希望)在其他地方进行了测试,可以在对对象本身进行单元测试时进行推测。您只真正对返回的对象是否具有正确的类型感兴趣,如果您的程序可以编译,则可以保证此类型。
由于工厂不会随时间变化(因为它们将是"构建者",这是另一种模式),因此没有要测试的命令。
工厂负责实例化对象,因此它们不应依赖其他工厂来为它们执行此操作。它们可能取决于构建器,但是即使如此,我们也不应该仅检验构建器是否收到消息来测试构建器的正确性。
这意味着您需要在工厂上进行的测试是它们是否将消息发送到它们所依赖的对象。如果使用依赖注入,这几乎是微不足道的。只需在单元测试中模拟依赖项,并验证它们是否收到消息。
单元测试工厂摘要
不要测试返回对象的行为或实现细节!您的工厂不负责对象实例的实现!
测试是否接收到发送给依赖项的命令。
就是这样。如果没有依赖项,则没有要测试的东西。除了可能断言返回的对象不是null引用。
集成测试工厂
如果您要求返回的抽象对象类型是特定具体类型的实例,则将其进行集成测试。
这里的其他人已经回答了使用instanceof运算符的方法。
由于我不知道您的工厂方法是什么样子,所以我现在只能建议
检查对象是否为您要寻找的正确的具体实现:
1 2
| IMyInterface fromFactory = factory.create(...);
Assert.assertTrue(fromFactory instanceof MyInterfaceImpl1); |
您可以检查工厂是否使用有效的实例变量设置了具体实例。
如果工厂返回具体实例,则可以使用@Parameters批注以获得更灵活的自动单元测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package it.sorintlab.pxrm.proposition.model.factory.task;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collection;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public class TaskFactoryTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{"sas:wp|repe" , WorkPackageAvailabilityFactory.class},
{"sas:wp|people", WorkPackagePeopleFactory.class},
{"edu:wp|course", WorkPackageCourseFactory.class},
{"edu:wp|module", WorkPackageModuleFactory.class},
{"else", AttachmentTaskDetailFactory.class}
});
}
private String fInput;
private Class<? extends TaskFactory> fExpected;
public TaskFactoryTest(String input, Class<? extends TaskFactory> expected) {
this.fInput = input;
this.fExpected = expected;
}
@Test
public void getFactory() {
assertEquals(fExpected, TaskFactory.getFactory(fInput).getClass());
}
} |
此示例是使用Junit4制作的。您会注意到,仅用一行代码就可以测试Factory方法的所有结果。
@ cem-catikkas我认为比较getClass()。getName()值会更正确。在MyInterfaceImpl1类是子类的情况下,您的测试可能会失败,因为该子类是MyInterfaceImpl1的instance。我将重写如下:
1 2
| IMyInterface fromFactory = factory.create(...);
Assert.assertEquals(fromFactory.getClass().getName(), MyInterfaceImpl1.class.getName()); |
如果您认为这样做可能会失败(我无法想象),请进行两次验证。
1 2 3 4
| if (myNewObject instanceof CorrectClass)
{
/* pass test */
} |
更新:
不知道为什么记下来了,所以我将其扩展一下...
1 2 3 4 5 6 7 8
| public void doTest()
{
MyInterface inst = MyFactory.createAppropriateObject();
if (! inst instanceof ExpectedConcreteClass)
{
/* FAIL */
}
} |