有关JUnit测试的字段的访问权限

时间:2017-05-17 13:51:44

标签: java unit-testing junit

假设我有一个以链表样式实现堆栈的类:

public class Stack {
    private StackNode base;

    public void push(Object item) {
        if (base == null) {base = new StackNode(item);}
        // remaining implementation not shown
    }
}

和它的JUnit测试类:

public class TestStack {
    @Test
    public void testPush() {
        Stack st = new Stack();
        st.push(new Integer(3)); 

        assertEquals((Integer)st.base.getContents(), new Integer(3)); //Error!
        // other test cases not shown
    }
}

我的问题是:解决测试类对base的访问权限问题的最佳做法是什么?

我知道这个question讨论了创建一个访问器,我知道我也可以将StackTestStack代码放入一个包中并生成{{1} } field package-private。

我想知道最好的行动方案是什么。我知道我不应该在base的单元测试中使用pop,反之亦然,因为这会结合这些单元测试。

我认为如果我不希望客户端代码访问该字段,那么创建(公共)访问器是不好的做法,正确的调用是为字段(或字段本身)包创建一个访问者-private?

另一个信息性question讨论了私有方法的选项,但这是关于一个公共方法,为了测试它我需要访问一个私有字段,所以“如果你需要访问私有方法/课程,你做错了“似乎并不适用于此。

2 个答案:

答案 0 :(得分:4)

我们应该编写运行公共API的测试。但这并不总是可行的。在这种情况下,我会编写一个测试,一起练习两个方法pushpop,因为这两个方法构成了一个功能堆栈:

public class Stack {
    private StackNode base;

    public void push(Object item) {
        ...
    }

    // I know this wasn't part of your code
    public Object pop() {
        ...
    }
}

public class TestStack {
    @Test
    public void testOnePush() {
        // GIVEN
        Stack st = new Stack();

        // WHEN
        st.push(new Integer(3)); 
        Object popped = st.pop();

        assertEquals(popped, new Integer(3));
    }
}

如果无法进行此类测试,则可以直接或通过getter公开base。在这种情况下,我更喜欢写一个警告评论,即它仅用于测试:

public class Stack {
    StackNode base; // package-private for testing reasons
}

也可以使用方法名称警告其他开发人员:

public class Stack {
    private StackNode base;

    /** For unit tests only!!! */
    public StackNode getBaseForTests() {
         return base;
    }
}

答案 1 :(得分:1)

您似乎已经阅读了很多问题,但没有看到消息的核心

核心要点是:您执行生产代码的测试内部。您有兴趣验证公共合同, what 。您希望通过验证如何避免这样做。

换句话说:如果你有一个实现堆栈的类,那么这个类的理想单元测试......只需检查该类对象的行为。含义:验证推送和弹出值是否会导致预期结果。弹出一个空堆栈会引发异常,......等等。

如果你真的想检查 部分,那么没有更改访问修饰符的解决方案就是使用Mockito及其@InjectMock注释。

换句话说:你可以模拟一个StackNode对象,而@InjectMock使用那个模拟的Stack做一些反射魔法给你一个StackNode对象。现在,您可以使用Mockito verify()方法确保此StackNode看到您希望它看到的调用。

但当然:这意味着您投入了很多的时间和精力来编写完全取决于Stack的实施细节的测试。一旦你决定改变它,整个单元测试就会中断;你也必须完全改写它。

这就是为什么你更喜欢测试 ,而不是

除此之外:对于您正在测试的类及其在相同包中的测试类生命(不在同一项目中),这是常见的最佳实践。对于一个名为// unit test only的字段,使用受包保护的getter也是一种非正式但令人惊讶的工作解决方案。