PowerMock:模拟私有静态最终变量,一个具体的例子

sam*_*lis 23 powermock

通过此测试必须完成的绝对最小模拟是什么?

码:

class PrivateStaticFinal {
    private static final Integer variable = 0;
    public static Integer method() { return variable + 1; }
}
Run Code Online (Sandbox Code Playgroud)

测试:

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

相关:测试类中的Mock私有静态最终变量(没有明确答案)

Wal*_*lls 40

免责声明:经过各种线程的大量搜索,我找到了答案.它可以做到,但一般的共识是它不是很安全,但看到你如何只在单位测试中做这个,我认为你接受这些风险:)


答案不是Mocking,因为大多数Mocking都不允许你进入决赛.答案是更"hacky",当你调用Java时,实际上是在修改私有字段是核心java.lang.reflect.Fieldjava.lang.reflect.Modifier类(反射).看看这个答案,我能够把剩下的测试拼凑在一起,而不需要嘲笑来解决你的问题.

这个答案的问题是我NoSuchFieldException在尝试修改时遇到的问题variable.对此的帮助在另一篇关于如何访问私有而非公开的领域的帖子中.

反射/场操作说明:

由于Mocking无法处理最终结果,而我们最终做的就是攻击该领域本身的根源.当我们使用Field操作(反射)时,我们正在寻找类/对象内的特定变量.一旦Java的发现它,我们得到了它的"调节剂",它告诉哪些限制/规则,它有一个像变量final,static,private,public,等我们找到合适的变量,然后告诉代码,它是可访问的,让我们改变这些修饰语.一旦我们改变了根目录的"访问"以允许我们操纵它,我们就会切换它的"最终"部分.然后,我们可以更改值并将其设置为我们需要的任何值.

简而言之,我们正在修改变量以允许我们更改其属性,删除属性final,然后更改值,因为它不再存在final.有关这方面的更多信息,请查看该想法来自的帖子.

所以我们一步一步地传递我们想要操作的变量并且......

// 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); 
Run Code Online (Sandbox Code Playgroud)

将这一切结合到一个新的SUPER ANSWER中.

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新 上述解决方案仅适用于在静态块中初始化的常量.当同时声明和初始化常量时,编译器可能会对其进行内联,此时将忽略对原始值的任何更改.

  • **注意**:只有在静态块中创建常量时,这才能可靠地工作.在同时声明和初始化常量时,编译器可能会对其进行内联,此时将忽略对原始值的任何更改. (3认同)