powermockito:如何在枚举中模拟抽象方法



请考虑以下(简化的(枚举:

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()的真正实现正在被称为。

我做错了什么?

  1. 枚举中的每个常量都是静态的最终嵌套类。所以要模拟它,你必须在PrepareForTest中指向嵌套类。
  2. MyEnum.values()返回预先初始化的数组,因此在您的情况下也应该模拟它。
  3. 每个 枚举常量 它只是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);
}
}

完整示例

这是使用枚举而不是"编译时常量"的关键 - 枚举类默认是最终的(你不能扩展MyEnum(。因此,在单元测试中处理它们可能很困难

@PrepareForTest意味着PowerMock将为带注释的类生成字节码。但是你不能同时拥有它:要么生成类(然后它不包含一个,两个,...(,要么它是"真实的" - 然后你不能覆盖行为。

因此,您的选择是:

  • 模拟整个类,然后看看是否可以 somhow getvalues()返回模拟枚举类对象列表(第一部分见这里(
  • 退后一步,改进您的设计。示例:您可以创建一个表示myMethod()接口,并让您的枚举实现它。然后你不直接使用values()- 而是引入某种简单地返回List<TheNewInterface>的工厂 - 然后工厂可以为你的单元测试返回模拟对象列表。

我强烈建议选项 2 - 因为它也会提高代码库的质量(通过切断与枚举类及其代码当前处理的常量的紧密耦合(。

根据我对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);
}
}

如您所见,测试非常简短且简单。但是,我建议不要嘲笑枚举,除非您有明确的需要这样做。不要仅仅因为它可以做到而嘲笑它。

最新更新