简体   繁体   中英

How to unit test graph nodes with mockito?

Consider the following class:

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);
    }
}

And then a unit test like:

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);
    }
}

Since registerDependent is private, mockito will not actually mock it. Since the mTarget is actually a real instance, when the registerDependency method is executed via initialize, it will try to execute the private method registerDependent on the mock. The mock being a mock will not be initialized and mDependants will actually be null causing a NullPointerException on mDependats.add(node).

What should be the correct way to test this? Should I use two real Nodes instead of a mock? should I make the methods public to allow the mocking of the method? Is there another option I'm missing?final Node node

Because this is a test for Node, avoid mocking Node if at all possible. It makes it far to easy to test that the mock framework works correctly, or that your spec is defined correctly, rather than testing if your implementation is correct.

I'm a fan of how JB Nizet put in his SO answer here : If you're building a bomb detonator, your frequent tests should use a real detonator and a mock bomb. The mocks should be for the dependencies and collaborators of your system under test, not the system under test itself.

If your Node were an interface, and your NodeImpl implementation could accept any Node as a dependent, then it may make more sense to use a mock Node—both because you could pass in Nodes with different implementations that might not even exist yet, and because many of Mockito's gotchas go away when you restrict yourself to mocking interfaces. However, because Node and its dependent Node are the same concrete class and rely on private implementation details, you'll probably have much more success working with real instances.

Besides, these Nodes are unlikely to involve heavy service layers or other dependencies that lend themselves to mocking, and there's no question whether the Node is well behaved: you can see that it is in your adjacent tests.

(Aside: There are techniques for mocking individual methods in your system under test—"partial mocking"—but those are good to avoid as well when you aren't working with legacy code or heavy services.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM