Java:单元测试级联外部接口

时间:2012-12-16 09:54:34

标签: java junit mocking mockito

考虑使用外部jar的类。该类处理D类型的对象,这些对象是通过对象ABC获得的,所有对象都来自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);

这看起来既麻烦又不优雅。还有更好的方法吗?

3 个答案:

答案 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,以便只模拟你的课程的过去,但这是对不同问题的讨论。