考虑以下类别:
public class Node {
private final Collection<Node> mDependants = new ArrayList<>();
private Node mDependency;
public void initialize(final Node node) {
// complex code that might call registerDependency;
}
private void registerDependency(final Node node) {
mDependency = node;
node.registerDependent(this);
}
private void registerDependent(final Node node) {
mDependants.add(node);
}
}
然后进行如下单元测试:
import static org.mockito.Mockito.mock;
public class NodeTest {
private Node mTarget;
private Node mDependent;
@Before
public void setUp() {
mTarget = new Node();
mDependent = mock(Node.class);
}
@Test
public void test() {
mTarget.initialize(mDependent);
}
}
由于registerDependent是私有的,因此mockito实际上不会对其进行模拟。由于mTarget实际上是真实实例,因此当通过initialize执行registerDependency方法时,它将尝试在该模拟上执行私有方法registerDependent。作为模拟的模拟将不会初始化,并且mDependant实际上将为null,从而导致mDependats.add(node)上出现NullPointerException。
什么是正确的测试方法?我应该使用两个真实的Node而不是模拟对象吗?我应该公开这些方法以允许该方法的模拟吗?我还缺少其他选择吗?“最终节点”节点
因为这是对Node的测试,所以请尽可能避免对Node进行模拟。它使测试模拟框架正常工作或规范定义正确变得很容易,而不是测试实现是否正确。
我很喜欢JB Nizet在这里的回答:如果您要建造炸弹雷管,那么经常进行的测试应使用真正的雷管和模拟炸弹。模拟应该针对被测系统的依赖性和协作者,而不是针对被测系统本身。
如果您的Node是一个接口,并且您的NodeImpl实现可以接受任何Node作为从属,那么使用模拟Node可能更有意义-两者都是因为您可以传入带有甚至可能还不存在的不同实现的Nodes,并且当您将自己限制为模拟接口时,许多Mockito的陷阱就会消失。但是,由于Node及其从属节点是相同的具体类,并且依赖于私有实现细节,因此使用实际实例可能会获得更大的成功。
此外,这些节点不太可能涉及繁重的服务层或其他依赖于模拟的依赖项,毫无疑问,该节点的行为是否良好:您可以看到它在相邻的测试中。
(此外:在被测系统中有一些模拟单个方法的技术,即“部分模拟”,但是当您不使用遗留代码或繁重的服务时,也应避免使用这些技术。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句