如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
当前回答
如果您确实需要直接测试私有方法/类等,那么可以使用其他答案中已经提到的反射。然而,如果说到这一点,我宁愿使用框架提供的实用程序类,而不是直接处理反射。例如,对于Java,我们有:
Spring框架中的ReflectionUtilsJUnit中的ReflectionUtils
关于如何使用它们,你可以在网上找到很多文章。我特别喜欢这个:
org.springframework.util.ReflectionUtils的Java代码示例
其他回答
JUnit.org常见问题解答页面的答案:
但如果你必须。。。如果您使用的是JDK1.3或更高版本,则可以使用反射来颠覆访问控制机制。有关如何使用它的详细信息,请阅读本文。如果您使用的是JDK1.6或更高版本,并且使用@测试时,可以使用Dp4j在测试方法中注入反射。对于有关如何使用它的详细信息,请参阅此测试脚本。
P.S.我是Dp4j的主要贡献者。问我你是否需要帮助。:)
对于C++(从C++11开始),将测试类添加为好友非常有效,不会破坏生产封装。
让我们假设我们有一些类Foo和一些真正需要测试的私有函数,还有一些类FooTest应该可以访问Foo的私有成员。然后我们应该写下以下内容:
// prod.h: some production code header
// forward declaration is enough
// we should not include testing headers into production code
class FooTest;
class Foo
{
// that does not affect Foo's functionality
// but now we have access to Foo's members from FooTest
friend FooTest;
public:
Foo();
private:
bool veryComplicatedPrivateFuncThatReallyRequiresTesting();
}
// test.cpp: some test
#include <prod.h>
class FooTest
{
public:
void complicatedFisture() {
Foo foo;
ASSERT_TRUE(foo.veryComplicatedPrivateFuncThatReallyRequiresTesting());
}
}
int main(int /*argc*/, char* argv[])
{
FooTest test;
test.complicatedFixture(); // and it really works!
}
从测试框架测试Java私有方法的最佳合法方法是在方法上添加@VisibleForTesting注释,因此相同的方法将像公共方法一样在测试框架中可见。
如果您有一些遗留的Java应用程序,并且不允许更改方法的可见性,那么测试私有方法的最佳方法是使用反射。
在内部,我们使用助手来获取/设置私有和私有静态变量,以及调用私有和私有的静态方法。以下模式将允许您执行与私有方法和字段相关的任何操作。当然,您不能通过反射来更改私有静态final变量。
Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);
对于字段:
Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
笔记:TargetClass.getDeclaredMethod(methodName,argClasses)允许您查看私有方法。同样的事情也适用于getDeclaredField。需要setAccessible(true)才能与private一起玩。
请参见下面的示例;
应添加以下导入语句:
import org.powermock.reflect.Whitebox;
现在,您可以直接传递具有私有方法、要调用的方法名和其他参数的对象,如下所示。
Whitebox.invokeMethod(obj, "privateMethod", "param1");