考虑使用外部jar的类。该类处理D
类型的对象,这些对象是通过对象A
,B
和C
获得的,所有对象都来自jar。
class DProcessor() {
public void process(PoolOfA pool) {
A a = pool.borrowObject()
...
B b = a.getB()
C c = b.getC()
for (D d : c.getAllDs()) {
// Do something meaningful with d
}
}
}
如何进行单元测试process(PoolOfA pool)
?
到目前为止,我最好的拍摄是为所有外部课程编写模拟:
PoolOfA pool = mock(PoolOfA.class);
A a = mock(A.class);
B b = mock(B.class);
C c = mock(C.class);
D d1 = mock(D.class);
D d2 = mock(D.class);
D d3 = mock(D.class);
D d4 = mock(D.class);
List listOfDs = new ArrayList<D>();
listOfDs.add(d1);
listOfDs.add(d2);
listOfDs.add(d3);
listOfDs.add(d4);
// Set specific behaviour for each d
when(pool.borrowObject()).thenReturn(a);
when(b.getC()).thenReturn(a);
when(c.getAllDs()).thenReturn(d);
when(b.getC()).thenReturn(c);
when(c.getAllDs()).thenReturn(listOfDs);
这看起来既麻烦又不优雅。还有更好的方法吗?
答案 0 :(得分:4)
当然,更好的方法是重写方法。但如果由于某种原因你无法做到这一点,mockito提供了一个很棒的功能,称为“深层存根”。查看docs。
答案 1 :(得分:2)
真正做的是在循环中处理一些Ds。我首先要通过更改签名来说清楚:
public void process(Collection<D> allDs)
现在,您可以通过仅模拟D来更轻松地测试。
如果该方法可以替换现有方法或包私有,则可以是公共方法,例如,如果您不想公开它。在后一种情况下,您可能仍希望测试另一个process
方法(采用poolOfA的方法)正确提取Ds。但这意味着process(PoolOfA)
需要了解很多poolOfA
似乎不对的内容。
这是this "Guide to writing testable code"提出的一个想法,我认为这些想法包含有趣的概念。您提到的内容可能属于“深入研究合作者”部分。
答案 2 :(得分:1)
我建议对进程方法进行一次小的重新设计:它目前负责做两件事:从输入的内部提取D的s并处理它们。
实际上,你的方法,尽管声明它期望输入类型为PoolOfA,但对这个对象绝对不感兴趣。它想要内在的东西。我将该方法声明为采用Iterable并将责任传递给调用者,以便为其提供正确的输入。这将阐明该方法的意图并使其更容易测试。
你可能会说:“这不是一个真正的解决方案,这只是将问题转移到另一个地方! 现在我需要测试调用方法!“
嗯,是的,不是。首先,请记住,您不必为了覆盖而使用UT。您应该将您的UT工作重点放在算法代码上,跳过琐碎的对象交互是可以的。
如果你坚持,你可以使用更强大的模拟库,如PowerMock,以便只模拟你的课程的过去,但这是对不同问题的讨论。