我有一个基本的 Widget
类,它使用实用程序类 Utils
public class Widget {
private static final String BUTTON_KEY = Utils.getMessage("btn-key");
public boolean comp() {
String specialKey = Utils.getMessage("special-key");
return specialKey.equals(BUTTON_KEY);
}
}
class Utils {
public static String getMessage(String key) {
return key + " : message";
}
}
我想为 comp
方法创建一个测试。
我的测试看起来像这样
@RunWith(PowerMockRunner.class)
@PrepareForTest(Utils.class)
public class WidgetTest {
private Widget widget;
@Before
public void setUp() {
mockStatic(Utils.class);
widget = new Widget();
}
@Test
public void testComp() {
expect(Utils.getMessage("btn-key")).andReturn("btn-key : message");
expect(Utils.getMessage("special-key")).andReturn("special-key : message");
replayAll();
assertFalse(widget.comp());
verifyAll();
}
}
测试失败
java.lang.IllegalStateException: missing behavior definition for the preceding method call:
Utils.getMessage("btn-key")
Usage is: expect(a.foo()).andXXX()
如果我删除常量字段中的方法调用(并因此删除对其的期望)测试成功。
问题是什么?
最佳答案
一般评论
请在回答其余部分之前阅读此哲学评论。
我经常遇到难以测试的代码。我们有 Spring,我们有依赖注入(inject),为什么我们仍然觉得需要使用静态方法和实用类?
当您想使用 PowerMock 时,请首先考虑对代码进行小幅重构是否无法更好地帮助您。
您需要模拟方法的事实意味着该方法并不是真正的静态。好吧,也许 Math.sqrt()
或 Assert.assertEquals()
是真正静态方法的好例子。
另一方面,一些乍一看似乎是“静态”的方法,一旦您开始考虑测试,就会背叛您,例如 LocalDate.now()
。在您的测试中,您需要静态的是当前日期,而不是方法:)
这里有很多好主意:https://softwareengineering.stackexchange.com/q/148049/105827
<小时/>你的问题
问题是 Widget
类的静态初始化器在您开始定义期望之前被调用。
如果将此代码添加到 Widget
类的任意位置,您可能会看到这一点:
static {
System.out.println("Widget.static");
}
将此代码添加到 WidgetTest.testComp()
方法的开头:
public void testComp() throws Exception {
System.out.println("test method");
... // the rest of the method
}
运行测试时,输出如下:
Widget.static
test method
这意味着 BUTTON_KEY = Utils.getMessage("btn-key")
执行之前 expect(Utils.getMessage("btn-key") ).andReturn("btn-key : message");
并且 PowerMock 正确地提示缺少行为定义。
可能的快速解决方案
如果您想保留静态逻辑,有一个快速的解决方法。不要在静态初始化程序 block 中启动 BUTTON_KEY,而是惰性启动。
我不太喜欢它,我还是更喜欢完全摆脱静态调用。
我在代码中留下了测试println()
,以便您可以看到调用的顺序。
public class Widget {
private static String BUTTON_KEY;
static {
System.out.println("Widget.static");
}
public boolean comp() {
String specialKey = Utils.getMessage("special-key");
return specialKey.equals(getButtonKey());
}
private static String getButtonKey() {
synchronized (Widget.class) {
if (BUTTON_KEY == null) {
System.out.println("Widget is calling Utils.getMessage(`btn-key`)");
BUTTON_KEY = Utils.getMessage("btn-key");
}
}
return BUTTON_KEY;
}
}
class Utils {
public static String getMessage(String key) {
return key + " : message";
}
}
调用的顺序是这样的:
Widget.static
test method
Widget is calling Utils.getMessage(`btn-key`)
关于java - 在类的 private static final 字段中模拟静态方法调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51112738/