我读了关于单元测试包装器方法的this answer。在以下代码中:
public static Object methodA(Object arg) {
if (arg == null) {
return null;
} else {
return methodB();
}
}
我不需要测试methodB()
中的所有功能,只测试arg
为空的2个案例,而不是因为methodB()
已经过测试,这似乎是合理的。
但是,如果methodB()
是私有方法,我应该测试methodB()
将提供的所有功能,因为根据another answer,私有方法是实现细节。
问题是,如果我有两个方法methodA1()
和methodA2()
,他们都会像这样调用methodB()
:
public static MyObject methodA1(int x) {
MyObject obj = new MyObject();
obj.setX(x);
return methodB(obj);
}
public static MyObject methodA2(int y) {
MyObject obj = new MyObject();
obj.setY(y);
return methodB(obj);
}
private static MyObject methodB(MyObject obj) {
// doSomething with obj
return obj;
}
我应该分别测试methodA1()
和methodA2()
吗?或者我可以测试私有方法methodB()
,因为methodA1()
和methodA2()
只是methodB()
的包装方法,所以如果正确测试methodB()
,我就赢了我需要测试methodA1()
和methodA2()
。
编辑:
我首先为公共方法编写了单独的测试。但问题是,如果methodA
存在许多变体,并且某些功能/需求在其中一些中共享,则测试用例的代码将被复制。这就是为什么我想知道我是否应该测试私有方法methodB()
。
我遇到的真正问题是我要求在数据库中添加记录,并且我应该提供许多不同的API。例如,我应该提供:
MyObject addMale(String name, String job); // fill sex with "Male"
MyObject addStudent(String name, String sex); // fill job with "Student"
所有这些都检查参数是否有效,填写未指定的字段并调用私有方法将记录实际插入数据库,即所谓的methodB()
。
有很多像这样的API,所以如果我只能测试methodB()
所有字段的情况,也许我可以减少测试用例的重复,但这是一个很好的做法来做这样的单元测试?
EDIT2:
我知道我可以测试私有方法,因为我在其他测试用例中使用了反射,而且我知道它也可以调用私有方法。但我的问题是在这种情况下,测试私有方法是否是一个合适的解决方案。
答案 0 :(得分:1)
这实际上是一段我一直想知道的问题,虽然它不一定正确,但我会分享我的观点。
主要问题是私人方法难以测试。我做了一些谷歌搜索,并有一些工具和技术,让你访问私人方法(反射是我遇到的主要方法),但他们似乎有点复杂。
但是你编写私有方法的原因是因为有另一种方法,一个公共方法,它调用它。因此,虽然您无法直接测试私有方法,但您可以通过测试调用它的公共方法来检查其功能。
如果我是你,我会广泛测试methodA1()
和methodA2()
,确保methodB()
的所有功能都通过您在公共方法上运行的测试进行测试。
答案 1 :(得分:1)
您已收到一些意见,为什么您应该为methodA1()
和methodA2()
编写单元测试;你已经推倒了它们。你在这里问了这个问题;你正在寻找一些不为他们编写完整的单元测试的理由。让我们看看我们是否可以给你你想要的答案。 ; - )
根据您在编辑中添加的内容,您可能会看到构建器模式的变体。例如)
MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build();
我们如何测试建造者?
由于构建器有4个属性,可以按任意顺序设置,因此会有4个属性! = 24种不同的排序。完整的测试套件必须包括:
@Test public void testXYZW() { ... }
@Test public void testXYWZ() { ... }
// ... 21 more permutations ...
@Test public void testWZYX() { ... }
但这就是全部吗?没有!其中一些属性可能具有默认值,因此我们也必须测试这些模式。总排序现在为P(4,4)+ P(4,3)+ P(4,2)+ P(4,1)+ P(4,0)= 24 + 24 + 12 + 4 + 1 = 85单元测试。
@Test public void testXWY() { ... }
@Test public void testWY() { ... }
@Test public void testZ() { ... }
// ... 61 more permutations ordering
这仅测试X,Y,Z,W属性的每个排列,每个测试每个属性一个测试值。为每种可能的组合和排列编写一套详尽的测试显然是笨拙的。
构建器类的设计者会理解属性设置的排列不会影响最终的构造;为订单的排列编写测试实际上并没有增加测试覆盖率。省略属性的测试很有用,因为它们测试默认值。再次测试省略属性的不同组合不会增加测试覆盖率。因此,仔细考虑之后,可能只需要进行两次测试:
@Test
public void testXYZW() {
MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build();
assertThat(o.wasBuiltProperly());
}
@Test void testDefaults() {
MyObject o = new Builder().build();
assertThat(o.wasBuiltProperlyFromDefaults());
}
如果正确完成methodB()
的完整测试,那么您可以放心地仅测试methodA1()
和methodA2()
中输入的验证。
@Test void testMethodA1Professor() {
MyObject o = methodA1("professor");
assertThat(o.wasBuiltProperlyWithProfessor());
}
@Test void testMethodA1WithNull() {
MyObject o = methodA1(null);
assertThat(o.wasBuiltProperlyWithNull());
}
答案 2 :(得分:0)
通常,当一个类中的多个方法使用私有方法时,隐藏在它背后的是一个独特的概念。它可能应该有自己的类。
不应该在课堂外使用
有几种方法可以将methodB
提取到自己的类中,而不是将其作为公共API的一部分提供:
将其嵌入使用它的类中并为其提供限制范围
将它放在较低级别的另一个模块中。使该模块可以从您的API获得,但不能从客户端获得。