如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
当前回答
对于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!
}
其他回答
下面是我测试私有字段的通用函数:
protected <F> F getPrivateField(String fieldName, Object obj)
throws NoSuchFieldException, IllegalAccessException {
Field field =
obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (F)field.get(obj);
}
我最近遇到了这个问题,并编写了一个名为Picklock的小工具,它避免了显式使用Java反射API的问题,两个示例:
通过Java反射调用方法,例如private void方法(String s)
Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");
通过Picklock调用方法,例如private void方法(String s)
interface Accessible {
void method(String s);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");
设置字段,例如私有BigInteger金额;-通过Java反射
Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));
设置字段,例如私有BigInteger金额;-由Picklock提供
interface Accessible {
void setAmount(BigInteger amount);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));
要用大型而古怪的类测试遗留代码,能够测试我现在正在编写的一个私有(或公共)方法通常非常有用。
我在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);
如果您有一些遗留的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一起玩。
JUnit.org常见问题解答页面的答案:
但如果你必须。。。如果您使用的是JDK1.3或更高版本,则可以使用反射来颠覆访问控制机制。有关如何使用它的详细信息,请阅读本文。如果您使用的是JDK1.6或更高版本,并且使用@测试时,可以使用Dp4j在测试方法中注入反射。对于有关如何使用它的详细信息,请参阅此测试脚本。
P.S.我是Dp4j的主要贡献者。问我你是否需要帮助。:)