Mocking Java enum to add a value to test fail case
I have an enum switch more or less like this:
public static enum MyEnum {A, B}
public int foo(MyEnum value) {
switch(value) {
case(A): return calculateSomething();
case(B): return calculateSomethingElse();
}
throw new IllegalArgumentException("Do not know how to handle " + value);
}
and I'd like to have all the lines covered by the tests, but as the code is expected to deal with all possibilities, I cannot supply a value without its corresponding case statement in the switch.
Extending the enum to add an extra value is not possible, and just mocking the equals method to return false
won't work either because the bytecode generated uses a jump table behind the curtains to go to the proper case... So I've thought that maybe some black magic could be achieved with PowerMock or something.
Thanks!
edit :
As I own the enumeration, I've thought that I could just add a method to the values and thus avoid the switch issue completely; but I'm leaving the question as it's still interesting.
Here is a complete example.
The code is almost like your original (just simplified better test validation):
public enum MyEnum {A, B}
public class Bar {
public int foo(MyEnum value) {
switch (value) {
case A: return 1;
case B: return 2;
}
throw new IllegalArgumentException("Do not know how to handle " + value);
}
}
And here is the unit test with full code coverage, the test works with Powermock (1.4.10), Mockito (1.8.5) and JUnit (4.8.2):
@RunWith(PowerMockRunner.class)
public class BarTest {
private Bar bar;
@Before
public void createBar() {
bar = new Bar();
}
@Test(expected = IllegalArgumentException.class)
@PrepareForTest(MyEnum.class)
public void unknownValueShouldThrowException() throws Exception {
MyEnum C = PowerMockito.mock(MyEnum.class);
Whitebox.setInternalState(C, "name", "C");
Whitebox.setInternalState(C, "ordinal", 2);
PowerMockito.mockStatic(MyEnum.class);
PowerMockito.when(MyEnum.values()).thenReturn(new MyEnum[]{MyEnum.A, MyEnum.B, C});
bar.foo(C);
}
@Test
public void AShouldReturn1() {
assertEquals(1, bar.foo(MyEnum.A));
}
@Test
public void BShouldReturn2() {
assertEquals(2, bar.foo(MyEnum.B));
}
}
Result:
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.628 sec
@Melloware
... code that executes the switch() statement java throws a java.lang.ArrayIndexOutOfBounds ...
I have this same Problem. Run your test with new Enum as first in your Test Class. I created bug with this Problem: https://code.google.com/p/powermock/issues/detail?id=440
Rather than using some radical bytecode manipulation to enable a test to hit the last line in foo
, I would remove it and rely on static code analysis instead. For example, IntelliJ IDEA has the "Enum switch
statement that misses case" code inspection, which would produce a warning for the foo
method if it lacked a case
.
上一篇: 使模板化优化更易于维护
下一篇: 嘲笑Java枚举添加一个值来测试失败案例