免责声明:经过大量寻找各种线程后,我找到了答案。可以做到,但普遍的共识是它不是很安全,但仅在单元测试中看到你是如何做到这一点的,我认为你接受这些风险:)
答案不是 Mocking,因为大多数 Mocking 不允许你闯入决赛。答案有点“hacky”,当 Java 调用核心 java.lang.reflect.Field 和 java.lang.reflect.Modifier 类(反射)时,您实际上是在修改私有字段。看着this answer,我能够拼凑出你剩下的测试,而不需要模拟来解决你的问题。
这个答案的问题是我在尝试修改variable 时遇到了NoSuchFieldException。这方面的帮助在于 another post 关于如何访问私有而不是公共的字段。
反射/场操作说明:
由于 Mocking 无法处理 final,因此我们最终要做的是侵入字段本身的根。当我们使用Field 操作(反射)时,我们正在寻找类/对象内部的特定变量。一旦 Java 找到它,我们就会得到它的“修饰符”,它告诉变量它有哪些限制/规则,例如 final、static、private、public 等。我们找到正确的变量,然后告诉代码它是可访问的,这允许我们更改这些修饰符。一旦我们更改了根的“访问”以允许我们对其进行操作,我们将关闭它的“最终”部分。然后我们可以更改该值并将其设置为我们需要的任何值。
简单地说,我们正在修改变量以允许我们更改其属性,删除final 的属性,然后更改值,因为它不再是final。欲了解更多信息,check out the post where the idea came from。
所以我们一步一步传入我们想要操作的变量,然后...
// 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);
将这一切组合成一个新的超级答案。
@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);
}
}
更新
上述解决方案仅适用于在静态块中初始化的常量。当同时声明和初始化常量时,可能会发生编译器内联它,此时对原始值的任何更改都会被忽略。