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

时间:2014-04-18 21:17:39

标签: powermock

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

代码:

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

试验:

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

相关:Mock private static final variables in the testing class(没有明确答案)

1 个答案:

答案 0 :(得分:38)

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


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

该答案的问题是我在尝试修改NoSuchFieldException时遇到variable。有关如何访问私有而非公开的字段的帮助{/ 3}}。

反思/现场操作说明:

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

简单地说,我们修改变量以允许我们更改其属性,删除final的属性,然后更改值,因为它不再是final。有关详细信息,请another post

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

// 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); 

将这一切结合到一个新的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);
    }
}

<强>更新 上述解决方案仅适用于在静态块中初始化的那些常量。当同时声明和初始化常量时,可能会发生编译器内联它,此时忽略对原始值的任何更改。