Java 基本方法为私有时的单元测试包装方法
我读过关于单元测试包装器方法的文章。在以下代码中:Java 基本方法为私有时的单元测试包装方法,java,unit-testing,tdd,Java,Unit Testing,Tdd,我读过关于单元测试包装器方法的文章。在以下代码中: public static Object methodA(Object arg) { if (arg == null) { return null; } else { return methodB(); } } 我不需要测试methodB()中的所有功能,只需测试arg为null的两种情况,而不是因为应该已经测试了methodB() 但是,如果methodB()是private方法,我应
public static Object methodA(Object arg) {
if (arg == null) {
return null;
} else {
return methodB();
}
}
我不需要测试methodB()
中的所有功能,只需测试arg
为null的两种情况,而不是因为应该已经测试了methodB()
但是,如果methodB()
是private方法,我应该测试methodB()
将提供的所有功能,因为根据,private方法是实现细节
问题是,如果我有两个方法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()
编辑:
起初,我为公共方法编写了单独的测试。但是问题是,如果方法a
有很多变体,并且其中一些功能/需求是共享的,那么测试用例的代码将被复制。这就是为什么我想知道是否应该测试私有方法methodB()
我遇到的真正问题是我需要在数据库中添加一条记录,并且我应该提供许多不同的API。例如,我应该提供:
MyObject addMale(String name, String job); // fill sex with "Male"
MyObject addStudent(String name, String sex); // fill job with "Student"
它们都检查参数是否有效,填充未指定的字段并调用private方法将记录实际插入数据库,这就是所谓的methodB()
有很多这样的API,所以如果我只能在所有字段的情况下测试methodB()
,也许我可以减少测试用例的重复,但是这样做单元测试是一种好的做法吗
编辑2:
我知道我可以测试私有方法,因为我在其他测试用例中使用了反射,我知道它也可以调用私有方法。但我的问题是在这种情况下,测试私有方法是否是一个合适的解决方案。这实际上是一个我想了一段时间的问题,虽然它不一定正确,但我将分享我的观点 主要问题是私有方法很难测试。我做了一些谷歌搜索,有一些工具和技术可以让你访问私有方法(反射是我遇到的主要方法),但是它们看起来有点复杂 但是您编写私有方法的原因是因为有另一个方法,一个公共方法,调用它。因此,虽然不能直接测试私有方法,但可以通过测试调用它的公共方法来检查其功能
如果我是你,我会广泛地测试
methodA1()
和methodA2()
,确保methodB()
的所有功能都通过在公共方法上运行的测试进行测试。你收到了一些关于为什么应该为methodA1()
和methodA2()编写单元测试的意见;你把他们推开了。你在这里问了这个问题;显然,您正在寻找一些不为它们编写完整单元测试的理由。让我们看看能否给你想要的答案。;-)
从您在编辑中添加的内容来看,您似乎在生成器模式上有一个变体。(例如)
我们如何测试构建器
由于生成器有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属性的每个排列,每个属性每个测试一个测试值。显然,为每一种可能的组合和排列编写一组详尽的测试是很不方便的
builder类的设计者会理解属性设置的排列不会影响结果的构造;为顺序排列编写测试实际上并没有增加测试覆盖率。对省略属性的测试很有用,因为它们测试默认值。再次测试省略属性的不同组合不会增加测试覆盖率。因此,经过仔细考虑,可能只需要两个测试:
@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()
中输入的验证
通常,当一个私有方法被一个类中的多个方法使用时,它背后隐藏着一个独特的概念。它应该有自己的班级
它不应该在课堂外使用
有几种方法可以将methodB
提取到它自己的类中,而不将其作为公共API的一部分提供:
- 将它嵌套在使用它的类中,并给它一个受限范围
- 将其放在较低级别的另一个模块中。使该模块可以从API中使用,但不能从客户端使用
如果methodA1
做了与methodA2
不同的事情,那么它显然需要单独测试。如果两种方法都做相同的事情,你只需要一个方法。稍微扩展一下:methodA1
调用对象上的setX
并将其传递给methodB
,其中asmethodA2
调用对象上的setY
,并将其传递给methodB
。根据是否设置了对象的X
,方法b的行为将有所不同,
@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());
}
@Test void testMethodA1Professor() {
MyObject o = methodA1("professor");
assertThat(o.wasBuiltProperlyWithProfessor());
}
@Test void testMethodA1WithNull() {
MyObject o = methodA1(null);
assertThat(o.wasBuiltProperlyWithNull());
}