假设我有一个以链表样式实现堆栈的类:
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讨论了创建一个访问器,我知道我也可以将Stack
和TestStack
代码放入一个包中并生成{{1} } field package-private。
我想知道最好的行动方案是什么。我知道我不应该在base
的单元测试中使用pop
,反之亦然,因为这会结合这些单元测试。
我认为如果我不希望客户端代码访问该字段,那么创建(公共)访问器是不好的做法,正确的调用是为字段(或字段本身)包创建一个访问者-private?
另一个信息性question讨论了私有方法的选项,但这是关于一个公共方法,为了测试它我需要访问一个私有字段,所以“如果你需要访问私有方法/课程,你做错了“似乎并不适用于此。
答案 0 :(得分:4)
我们应该编写运行公共API的测试。但这并不总是可行的。在这种情况下,我会编写一个测试,一起练习两个方法push
和pop
,因为这两个方法构成了一个功能堆栈:
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也是一种非正式但令人惊讶的工作解决方案。