如何使用 mockito 对图形节点进行单元测试?

如何使用 mockito 对图形节点进行单元测试?

问题描述:

考虑以下类:

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实际上是一个真实的实例,当registerDependency方法通过initialize执行时,它会尝试在mock上执行私有方法registerDependent.作为模拟的模拟将不会被初始化,并且 mDependants 实际上将为 null 导致 mDependats.add(node) 上的 NullPointerException.

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

因为这是对 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.

我很喜欢 JB Nizet 如何在 他在这里给出的答案:如果你正在制造炸弹雷管,您的频繁测试应该使用雷管和模拟炸弹.模拟应该用于被测系统的依赖项合作者,而不是被测系统本身.

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.

如果您的 Node 是一个接口,并且您的 NodeImpl 实现可以接受任何 Node 作为依赖项,那么使用模拟 Node 可能更有意义——因为您可以传入具有不同实现的 Node,而这些实现甚至可能还不存在,并且因为当您限制自己使用模拟接口时,Mockito 的许多问题就会消失.但是,因为 Node 和它的依赖 Node 是同一个具体的类并且依赖于私有的实现细节,所以你可能会在处理真实实例时取得更大的成功.

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.)