1 概述
本文会首先简要说明为什么直接对私有方法测试不是一个好的实践。其次演示如果在必要的情况下如何测试私有方法
2 为什么不应该直接测试私有方法
我们写的单测应该只检查我们的公共方法。首先公有方法的调用者对公有方法内部使用的私有方法是无感知的。其次,好的设计对外接口应该是稳定的,在接口稳定的状态下,改变私有方法的实现不应该影响接口定义,因此也不应该影响我们的测试。
但有时也会遇到必须测试测试私有方法的情况,常见的场景如下:
- 在私有方法中存在通过公有方法入口测试无法完成行覆盖的“死”代码。
- 私有方法设计不合理,实现太复杂,根本没法通过测试完整覆盖,代码不可测。
因此,当我们觉得需要测试私有方法时,先考虑一下设计是否合理。
3 从私有方法中删除无法覆盖的代码
下面是一段需要测试的代码,公共方法validateAndDouble依赖doubleInteger。
private static Integer doubleInteger(Integer input) {
if (input == null) {
return null;
}
return 2 * input;
}
public static Integer validateAndDouble(Integer input) {
if (input == null) {
throw new IllegalArgumentException("input should not be null");
}
return doubleInteger(input);
}
我们写的单测如下
@Test
void givenNull_WhenValidateAndDouble_ThenThrows() {
assertThrows(IllegalArgumentException.class, () -> validateAndDouble(null));
}
@Test
void givenANonNullInteger_WhenValidateAndDouble_ThenDoublesIt() {
assertEquals(4, validateAndDouble(2));
}
接着通过JaCoCo插件检查覆盖率发现有段代码未覆盖到。
但仔细思考可以看到未覆盖代码实际并没有必要,因为外层已经做过检查,因此直接删除未覆盖代码即可。
4 对私有方法测试
假设不考虑合理性设计问题,那我们可以通过反射来对私有方法测试。
private Method getDoubleIntegerMethod() throws NoSuchMethodException {
Method method = Utils.class.getDeclaredMethod("doubleInteger", Integer.class);
method.setAccessible(true);
return method;
}
@Test
void givenNull_WhenDoubleInteger_ThenNull() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
assertEquals(null, getDoubleIntegerMethod().invoke(null, new Integer[] { null }));
}
@Test
void givenANonNullInteger_WhenDoubleInteger_ThenDoubleIt() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
assertEquals(74, getDoubleIntegerMethod().invoke(null, 37));
}
5 总结
本文简要讲了为什么测试私有方法不是一个好的实践,其次演示了如果一定要测试私有方法,如何通过反射的方式进行测试。