如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
当前回答
您可以创建一个特殊的公共方法来代理要测试的私有方法。使用IntelliJ时,@TestOnly注释是现成的。缺点是,如果有人想在公共环境中使用私有方法,他可以这样做。但注释和方法名会警告他。在IntelliJ上,执行此操作时将显示警告。
import org.jetbrains.annotations.TestOnly
class MyClass {
private void aPrivateMethod() {}
@TestOnly
public void aPrivateMethodForTest() {
aPrivateMethod()
}
}
其他回答
要用大型而古怪的类测试遗留代码,能够测试我现在正在编写的一个私有(或公共)方法通常非常有用。
我在Java中使用junitx.util.PrivateAccessor包。它有很多用于访问私有方法和私有字段的有用的一行程序。
import junitx.util.PrivateAccessor;
PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);
在C++中:在包含包含要测试的私有函数的类头之前。
使用此代码:
#define private public
#define protected public
在尝试了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:静态内部类以获取乐趣和利润
这是我的龙目样本:
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Student student = new Student();
Field privateFieldName = Student.class.getDeclaredField("name");
privateFieldName.setAccessible(true);
privateFieldName.set(student, "Naruto");
Field privateFieldAge = Student.class.getDeclaredField("age");
privateFieldAge.setAccessible(true);
privateFieldAge.set(student, "28");
System.out.println(student.toString());
Method privateMethodGetInfo = Student.class.getDeclaredMethod("getInfo", String.class, String.class);
privateMethodGetInfo.setAccessible(true);
System.out.println(privateMethodGetInfo.invoke(student, "Sasuke", "29"));
}
@Setter
@Getter
@ToString
class Student {
private String name;
private String age;
private String getInfo(String name, String age) {
return name + "-" + age;
}
}
请参见下面的示例;
应添加以下导入语句:
import org.powermock.reflect.Whitebox;
现在,您可以直接传递具有私有方法、要调用的方法名和其他参数的对象,如下所示。
Whitebox.invokeMethod(obj, "privateMethod", "param1");