2

考虑一个使用外部 jar 的类。该类处理类型D为 的对象,这些对象是通过对象AB和获得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);

这看起来既麻烦又不优雅。有没有更好的办法?

4

3 回答 3

4

Better way is to rewrite the method, of course. But if you cannot do it for some reason, mockito offers great feature called 'deep stubs'. Check out the docs.

于 2012-12-16T10:13:44.463 回答
2

真正的过程是在循环中处理一些 D。我首先要通过更改签名来说明:

public void process(Collection<D> allDs)

现在您可以通过仅模拟 D 来更轻松地测试它。

That method can either be public if it can replace the existing one or package private for example if you don't want to expose it. In that latter case, you might still want to test that the other process method (the one that takes a poolOfA) properly extract the Ds. But that means that process(PoolOfA) needs to know a lot about the poolOfA which does not seem right.

This is one of the ideas promoted by this "Guide to writing testable code" which I think contains interesting concepts. What you mention would probably fall into the "Digging into Collaborators" section.

于 2012-12-16T10:06:08.497 回答
1

I would suggest a small redesign of the process method: It is currently responsible for doing two things: Extracting an Iterable of D's from the internals of the input and processing them.

So actually, you method, although declaring that it is expecting an input of type PoolOfA, is absolutely not interested in this object. It wants what's inside. I wold declare the method as taking an Iterable and pass the responsibility to the caller to give it the correct input. This will clarify the intention of the method and make it easier to test.

You may say: "this is not a real solution, this is just moving the problem to another location! now I need to test the calling method!"

Well, yes and no. First, remember that you do not have to UT everything, just for the sake of coverage. You should focus your UT efforts on algorithmic pieces of code, it is OK to skip trivial object interactions.

If you insist, you can use more powerful mocking libraries like PowerMock in order to mock only past of your class, but this is a discussion for a different Question.

于 2012-12-16T10:09:31.077 回答