PowerMock:模拟出私有静态最终变量,一个具体示例 [英] PowerMock: mock out private static final variable, a concrete example

查看:1536
本文介绍了PowerMock:模拟出私有静态最终变量,一个具体示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要通过此测试,必须要做的绝对最小模拟是什么?



代码:

  class PrivateStaticFinal {
private static final Integer variable = 0;
public static Integer method(){返回变量+ 1; }
}

测试:

  @RunWith(PowerMockRunner.class)
@PrepareForTest(PrivateStaticFinal.class)
类PrivateStaticFinalTest {
@Test
public void testMethod( ){
// TODO PrivateStaticFinal.variable = 100
assertEquals(PrivateStaticFinal.method(),101);
}
}

相关:在测试类中模拟私有静态最终变量(无明确答案)

解决方案

免责声明:在各种线程上进行了很多摸索之后,我找到了答案。可以做到,但是普遍的共识是它不是很安全,但是仅在单元测试中,我认为您是如何做到这一点的,我认为您会接受这些风险:)



< hr>

答案不是嘲笑,因为大多数嘲笑都不允许您闯入决赛。答案有些棘手,实际上,您正在修改Java调用时的私有字段,它是核心 java.lang.reflect.Field java.lang.reflect.Modifier 类(反射)。看着这个答案能够组合其余的测试,而无需进行模拟即可解决您的问题。



该答案的问题是我遇到了 NoSuchFieldException 尝试修改变量时。有关帮助的内容,请参见另一篇文章,其中介绍了如何访问私有而非公开的字段。



反射/场操作说明:



由于模拟无法处理最终结果,相反,我们最终要做的是侵入该领域本身的根源。当使用 Field 操纵(反射)时,我们正在寻找类/对象内的特定变量。 Java找到它后,我们将获得它的修饰符,它告诉变量它具有什么限制/规则,如 final static 私有公共等。我们找到正确的变量,然后告诉代码它是可访问性,可让我们更改这些修饰符。一旦更改了根目录下的访问权限以允许我们对其进行操作,我们便会切换到它的最终部分。然后,我们可以更改值并将其设置为我们需要的任何值。



简单地说,我们正在修改变量以允许我们更改其属性,从而消除了吸引力为 final ,然后更改值,因为它不再是 final 。有关此的更多信息,请查看该创意的来源



因此,我们逐步传递了要操作的变量并...

  //将字段标记为公共字段,以便我们可以用它来玩味
field.setAccessible(true);
//获取字段的修饰符
字段修饰符Field = Field.class.getDeclaredField( modifiers);
//允许我们更改修饰符
ModifysField.setAccessible(true);
//通过删除修饰符中说 FINAL的位来从字段中删除最终修饰符
修饰符Field.setInt(field,field.getModifiers()&〜Modifier.FINAL);
//设置新值
field.set(null,newValue);






将所有内容组合成一个新的超级答案

  @RunWith(PowerMockRunner.class)
@PrepareForTest()
class PrivateStaticFinalTest {
@Test
public void testMethod(){
try {
setFinalStatic(PrivateStaticFinal.class.getDeclaredField( variable),Integer.valueOf(100));
}
catch(SecurityException e){fail();}
catch(NoSuchFieldException e){fail();}
catch(Exception e){fail();}
assertEquals(PrivateStaticFinal.method(),Integer.valueOf(101));
}

静态无效setFinalStatic(Field field,Object newValue)引发异常{
field.setAccessible(true);
//从字段
中删除最终修饰符Field ModifysField = Field.class.getDeclaredField( modifiers);
修饰符Field.setAccessible(true);
修饰符Field.setInt(field,field.getModifiers()&〜Modifier.FINAL);
field.set(null,newValue);
}
}

更新
以上解决方案仅适用于在静态块中初始化的常量,同时声明和初始化常量时,编译器会内联它,这时可能会忽略对原始值的任何更改。 / p>

what is the absolute minimal mocking that must be done to pass this test?

code:

class PrivateStaticFinal {
    private static final Integer variable = 0;
    public static Integer method() { return variable + 1; }
}

test:

@RunWith(PowerMockRunner.class)
@PrepareForTest(PrivateStaticFinal.class)
class PrivateStaticFinalTest {
    @Test
    public void testMethod() {
        //TODO PrivateStaticFinal.variable = 100
        assertEquals(PrivateStaticFinal.method(), 101);
    }
}

related: Mock private static final variables in the testing class (no clear answer)

解决方案

Disclaimer: After a lot of hunting around on various threads I have found an answer. It can be done, but the general concensus is that it is not very safe but seeing as how you are doing this ONLY IN UNIT TESTS, I think you accept those risks :)


The answer is not Mocking, since most Mocking does not allow you to hack into a final. The answer is a little more "hacky", where you are actually modifying the private field when Java is calling is core java.lang.reflect.Field and java.lang.reflect.Modifier classes (reflection). Looking at this answer I was able to piece together the rest of your test, without the need for mocking that solves your problem.

The problem with that answer is I was running into NoSuchFieldException when trying to modify the variable. The help for that lay in another post on how to access a field that was private and not public.

Reflection/Field Manipulation Explained:

Since Mocking cannot handle final, instead what we end up doing is hacking into the root of the field itself. When we use the Field manipulations (reflection), we are looking for the specific variable inside of a class/object. Once Java finds it we get the "modifiers" of it, which tell the variable what restrictions/rules it has like final, static, private, public, etc. We find the right variable, and then tell the code that it is accessible which allows us to change these modifiers. Once we have changed the "access" at the root to allow us to manipulate it, we are toggling off the "final" part of it. We then can change the value and set it to whatever we need.

To put it simply, we are modifying the variable to allow us to change its properties, removing the propety for final, and then changing the value since it is no longer final. For more info on this, check out the post where the idea came from.

So step by step we pass in the variable we want to manipulate and...

// Mark the field as public so we can toy with it
field.setAccessible(true);
// Get the Modifiers for the Fields
Field modifiersField = Field.class.getDeclaredField("modifiers");  
// Allow us to change the modifiers
modifiersField.setAccessible(true);
 // Remove final modifier from field by blanking out the bit that says "FINAL" in the Modifiers
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
// Set new value
field.set(null, newValue); 


Combining this all into a new SUPER ANSWER you get.

@RunWith(PowerMockRunner.class)
@PrepareForTest()
class PrivateStaticFinalTest {
    @Test
    public void testMethod(){
      try {
        setFinalStatic(PrivateStaticFinal.class.getDeclaredField("variable"), Integer.valueOf(100));
      } 
      catch (SecurityException e) {fail();}
      catch (NoSuchFieldException e) {fail();}
      catch (Exception e) {fail();}
      assertEquals(PrivateStaticFinal.method(), Integer.valueOf(101));
    }

    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        // remove final modifier from field
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }
}

Update The above solution will work only for those constants which is initialized in static block.When declaring and initializing the constant at the same time, it can happen that the compiler inlines it, at which point any change to the original value is ignored.

这篇关于PowerMock:模拟出私有静态最终变量,一个具体示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆