考虑以下(简化)枚举:
MyEnum {
ONE public int myMethod() {
// Some complex stuff
return 1;
},
TWO public int myMethod() {
// Some complex stuff
return 2;
};
public abstract int myMethod();
}
这用于以下功能:
void consumer() {
for (MyEnum n : MyEnum.values()) {
n.myMethod();
}
}
我现在想为consumer
编写一个单元测试,在每个枚举实例中模拟对myMethod()的调用。我尝试了以下内容:
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyEnum.class)
public class MyTestClass {
@Test
public void test() throws Exception {
mockStatic(MyEnum.class);
when(MyEnum.ONE.myMethod()).thenReturn(10);
when(MyEnum.TWO.myMethod()).thenReturn(20);
// Now call consumer()
}
但是正在调用ONE.myMethod()
和TWO.myMethod()
的实际实现。
我做错了什么?
答案 0 :(得分:4)
MyEnum.values()
返回预先初始化的数组,所以在你的情况下它也应该是mock。 public final static
字段。所有在一起:
@RunWith(PowerMockRunner.class)
@PrepareForTest(
value = MyEnum.class,
fullyQualifiedNames = {
"com.stackoverflow.q45414070.MyEnum$1",
"com.stackoverflow.q45414070.MyEnum$2"
})
public class MyTestClass {
@Test
public void should_return_sum_of_stubs() throws Exception {
final MyEnum one = mock(MyEnum.ONE.getClass());
final MyEnum two = mock(MyEnum.TWO.getClass());
mockStatic(MyEnum.class);
when(MyEnum.values()).thenReturn(new MyEnum[]{one, two});
when(one.myMethod()).thenReturn(10);
when(two.myMethod()).thenReturn(20);
assertThat(new Consumer().consumer())
.isEqualTo(30);
}
@Test
public void should_return_stubs() {
final MyEnum one = mock(MyEnum.ONE.getClass());
when(one.myMethod()).thenReturn(10);
Whitebox.setInternalState(MyEnum.class, "ONE", one);
assertThat(MyEnum.ONE.myMethod()).isEqualTo(10);
}
}
答案 1 :(得分:3)
这是使用枚举超过"编译时间常数"的关键。 - 枚举类默认是最终的(你不能扩展MyEnum)。因此,在单元测试中处理它们可以 hard 。
@PrepareForTest意味着PowerMock将为带注释的类生成字节代码。但是你不能两种方式:要么生成(然后它不包含ONE,TWO,......)或者它是"真实&# 34; - 然后你就无法覆盖行为。
所以你的选择是:
values()
返回一个模拟的枚举类对象列表(参见here的第一部分)myMethod()
的接口,并让您的枚举实现它。然后你不能直接使用values()
- 而是引入某种只返回List<TheNewInterface>
的工厂 - 然后工厂可以为你的单元测试返回一个模拟对象列表。 / LI>
我强烈推荐选项2 - 因为这也将提高代码库的质量(通过减少与枚举类及其代码当前处理的常量的紧密耦合)。
答案 2 :(得分:0)
根据我对PowerMock的了解,您的测试应按原样运行。也许你可以在PowerMock github项目中打开一个问题?
无论如何,这是一个 工作的自包含测试,但使用另一个库,JMockit:
public final class MockingAnEnumTest {
public enum MyEnum {
ONE { @Override public int myMethod() { return 1; } },
TWO { @Override public int myMethod() { return 2; } };
public abstract int myMethod();
}
int consumer() {
int res = 0;
for (MyEnum n : MyEnum.values()) {
int i = n.myMethod();
res += i;
}
return res;
}
@Test
public void mocksAbstractMethodOnEnumElements() {
new Expectations(MyEnum.class) {{
MyEnum.ONE.myMethod(); result = 10;
MyEnum.TWO.myMethod(); result = 20;
}};
int res = consumer();
assertEquals(30, res);
}
}
如您所见,测试非常简短。但是,除非您明确需要,否则我建议不模拟枚举。不要因为可以做到而嘲笑它。