我的Android项目有两个UnitTest项目。一个用于JUnit测试,一个用于Android单元测试。在JUnit测试项目中,我创建了一个类来访问或设置私有字段,方法或构造函数。 (PS:对于那些对完整代码感到好奇的人,请告诉我,我会将其添加到本文的底部。)
我还有UnitTests来测试这些私有方法访问。现在所有这些UnitTests都可以工作,接受一个:设置最终静态字段的值。
这是我用来设置私有字段的方法:
// Test method to set a private Field from a class
public static void setPrivateField(Object ob, String fieldName, Object value) throws MyUnitTestException{
try {
Field field = ob.getClass().getDeclaredField(fieldName);
if(field != null){
field.setAccessible(true);
if(Modifier.isFinal(field.getModifiers())){
Field modifierField = Field.class.getDeclaredField("modifiers");
modifierField.setAccessible(true);
modifierField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
/*int modifiers = field.getModifiers();
Field modifierField = field.getClass().getDeclaredField("modifiers");
modifiers = modifiers & ~Modifier.FINAL;
modifierField.setAccessible(true);
modifierField.setInt(field, modifiers);*/
}
// ** IllegalAccessException at the following line with final static fields:
field.set(ob, value); // static fields ignore the given Object-parameter
}
}
catch (NoSuchFieldException ex){
throw new MyUnitTestException(ex);
}
catch (IllegalAccessException ex){
throw new MyUnitTestException(ex);
}
catch (IllegalArgumentException ex){
throw new MyUnitTestException(ex);
}
}
这是UnitTest:
@Test
public void testSetIntFields(){
MyClass myClassInstance = new MyClass();
final int value = 5;
for(int nr = 1; nr <= 4; nr++){
String nameOfField = "myInt" + nr;
try {
TestMethodsClass.setPrivateField(myClassInstance, nameOfField, value);
}
catch (MyUnitTestException ex) {
Assert.fail("setPrivateField caused an Exception: " + ex.getThrownException());
}
int x = myClassInstance.getMyInt(nr);
Assert.assertTrue("myInt " + nr + " should be above 0", x > 0);
Assert.assertEquals("myInt " + nr + " should equal the set value (" + value + ")", value, x);
}
}
使用以下MyClass:
@SuppressWarnings("unused")
public class MyClass
{
private int myInt1 = 0;
private static int myInt2 = 0;
private final int myInt3 = 0;
private static final int myInt4 = 0;
public MyClass(){ }
public int getInt(int nr){
switch(nr){
case 1:
return myInt1;
case 2:
return myInt2;
case 3:
return myInt3;
case 4:
return myInt4;
}
return -1;
}
}
(以及以下MyUnitTestException):
public class MyUnitTestException extends Exception
{
private static final long serialVersionUID = 1L;
private Throwable thrownException;
public MyUnitTestException(Throwable ex){
super(ex);
thrownException = ex;
}
public String getThrownException(){
if(thrownException != null)
return thrownException.getClass().getName();
else
return null;
}
}
将值设置为字段myInt1
,myInt2
和myInt3
有效,但在myInt4
我收到 IllegalAccessException 。< / p>
有谁知道我应该如何在我的setPrivateField方法中解决这个问题?因此,它不仅可以设置private
,private static
和private final
字段,还可以设置private static final
个字段。
编辑1:
在阅读本文Forbidden Java actions: updating final and static final fields关于RunTime的内嵌后,我将UnitTest修改为:
@Test
public void testSetIntFields(){
MyClass myClassInstance = new MyClass();
final int value = 5;
for(int nr = 1; nr <= 4; nr++){
String nameOfField = "myInt" + nr;
try {
TestMethodsClass.setPrivateField(myClassInstance, nameOfField, value);
}
catch (MyUnitTestException ex) {
Assert.fail("setPrivateField caused an Exception: " + ex.getThrownException());
}
// Get the set value using reflection
// WARNING: Since at RunTime in-lining occurs, we never use a Getter to test the set value, but instead use reflection again
int x = -1;
try {
x = (Integer)TestMethodsClass.getPrivateField(myClassInstance, nameOfField);
}
catch (MyUnitTestException ex) {
Assert.fail("getPrivateField caused an Exception: " + ex.getThrownException());
}
Assert.assertTrue("myInt " + nr + " should be above 0", x > 0);
Assert.assertEquals("myInt " + nr + " should equal the set value (" + value + ")", value, x);
}
}
(这是我的getPrivateField方法,已经完全测试并且有效):
// Test method to access a private Field from a class
public static Object getPrivateField(Object ob, String fieldName) throws MyUnitTestException{
Object returnObject = null;
try {
Field field = ob.getClass().getDeclaredField(fieldName);
if(field != null){
field.setAccessible(true);
returnObject = field.get(ob); // static fields ignore the given Object-parameter
}
}
catch (NoSuchFieldException ex) {
throw new MyUnitTestException(ex);
}
catch (IllegalAccessException ex) {
throw new MyUnitTestException(ex);
}
catch (IllegalArgumentException ex) {
throw new MyUnitTestException(ex);
}
return returnObject;
}
但我仍然得到同样的错误。
编辑2:
因为我在它上面的UnitTest中使用了getPrivateField并同时测试了我的所有UnitTests,所以它没有用。当我单独测试上面的UnitTest时,它确实有效。所以我删除了getPrivateField-UnitTests(因为在上面的代码中我在一次测试中同时使用Set和Get),现在它确实有效。
我知道这对于UnitTests来说是非常糟糕的做法,但是在RunTime期间更改私有静态final字段无论如何都是不好的做法。我刚刚创建了类来获取和设置私有字段,方法和构造函数,因为我在一些单元测试中需要它大约3-4次然后我只是好奇你可以用反射进行多远,并为我创建一个TestCase可以想到。 (我个人认为它有点太过分了。)
警告:在测试之外的任何其他情况下都不要使用反射。我建议不要在正常项目中使用它,除非你已经尝试过其他所有方法。 (除了测试之外,我甚至无法想到你想在项目中使用反射的情况。)
答案 0 :(得分:4)
特殊处理原始静态最终字段。
编译器将其内联为常量。最终的可执行文件不再在运行时访问该字段。
答案 1 :(得分:0)
static final
字段在编译器中是特殊的,因为它允许内联到任何调用它的方法中。
最终代码中甚至可能不存在。
static final int TEN = 10; // removed at compile time
int twenty() {
return TEN * 2; // compiler will turn this into 10*2 (and then probably 20 directly)
}