如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
当前回答
私有方法被公共方法使用。否则,它们就是死代码。这就是为什么要测试公共方法,断言公共方法的预期结果,从而断言它使用的私有方法。
在公共方法上运行单元测试之前,应该通过调试来测试私有方法。
也可以使用测试驱动开发来调试它们,调试单元测试,直到满足所有断言。
我个人认为最好使用TDD创建类;创建公共方法存根,然后使用预先定义的所有断言生成单元测试,这样在编写代码之前就可以确定方法的预期结果。这样,就不会走上使单元测试断言符合结果的错误道路。然后,当所有单元测试通过时,您的类是健壮的并满足要求。
其他回答
在尝试了Cem Catikkas使用Java反射的解决方案后,我不得不说,他的解决方案比我在这里描述的更优雅。然而,如果您正在寻找使用反射的替代方案,并且能够访问您正在测试的源代码,那么这仍然是一个选项。
测试类的私有方法可能有好处,特别是在测试驱动开发中,您希望在编写任何代码之前设计小型测试。
创建一个可以访问私有成员和方法的测试,可以测试那些只访问公共方法而难以专门针对的代码区域。如果公共方法涉及多个步骤,它可以由多个私有方法组成,然后可以单独测试。
优势:
可以测试到更精细的粒度
缺点:
测试代码必须位于文件作为源代码更难维护与.class输出文件类似,它们必须保持在源代码中声明的相同包中
然而,如果连续测试需要这种方法,这可能是一个信号,表明应该提取私有方法,可以以传统的公共方式进行测试。
下面是一个复杂的例子,说明这是如何工作的:
// Import statements and package declarations
public class ClassToTest
{
private int decrement(int toDecrement) {
toDecrement--;
return toDecrement;
}
// Constructor and the rest of the class
public static class StaticInnerTest extends TestCase
{
public StaticInnerTest(){
super();
}
public void testDecrement(){
int number = 10;
ClassToTest toTest= new ClassToTest();
int decremented = toTest.decrement(number);
assertEquals(9, decremented);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(StaticInnerTest.class);
}
}
}
内部类将编译为ClassToTest$StaticInnerTest。
另请参阅:Java提示106:静态内部类以获取乐趣和利润
正如其他人所说。。。不要直接测试私有方法。以下是一些想法:
保持所有方法的规模和重点(易于测试,易于发现错误)使用代码覆盖工具。我喜欢Cobertura(哦,快乐的一天,它看起来像是一个新版本!)
在单元测试上运行代码覆盖率。如果您发现方法没有经过充分测试,请添加到测试中以提高覆盖率。目标是100%的代码覆盖率,但要意识到你可能无法做到这一点。
在Spring Framework中,您可以使用以下方法测试私有方法:
ReflectionTestUtils.invokeMethod()
例如:
ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");
下面的ReflectionTestUtil可以通用地用于测试私有方法的原子性。
import com.google.common.base.Preconditions;
import org.springframework.test.util.ReflectionTestUtils;
/**
* <p>
* Invoker
* </p>
*
* @author
* @created Oct-10-2019
*/
public class Invoker {
private Object target;
private String methodName;
private Object[] arguments;
public <T> T invoke() {
try {
Preconditions.checkNotNull(target, "Target cannot be empty");
Preconditions.checkNotNull(methodName, "MethodName cannot be empty");
if (null == arguments) {
return ReflectionTestUtils.invokeMethod(target, methodName);
} else {
return ReflectionTestUtils.invokeMethod(target, methodName, arguments);
}
} catch (Exception e) {
throw e;
}
}
public Invoker withTarget(Object target) {
this.target = target;
return this;
}
public Invoker withMethod(String methodName) {
this.methodName = methodName;
return this;
}
public Invoker withArguments(Object... args) {
this.arguments = args;
return this;
}
}
Object privateMethodResponse = new Invoker()
.withTarget(targetObject)
.withMethod(PRIVATE_METHOD_NAME_TO_INVOKE)
.withArguments(arg1, arg2, arg3)
.invoke();
Assert.assertNotNutll(privateMethodResponse)
我和我的团队正在使用Typemock,它有一个API,允许您伪造非公共方法。
最近,他们增加了伪造不可见类型和使用xUnit的能力。