Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 607
fortran Avatar asked Mar 16 '11 09:03

fortran


People also ask

Can we mock enum in Java?

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...

Can enum class be mocked?

It is possible, but this is not recommended, it would be better to refactor the code.

Can we add values in enum?

No, it can't as this would mean that you would be able to modify existing types at runtime which you can't. Why you say you cant add values to an existing enum if the other answears are adding?

Can we add constants to enum without breaking existing code?

4) Adding new constants on Enum in Java is easy and you can add new constants without breaking the existing code.


2 Answers

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 = mock(MyEnum.class);         when(C.ordinal()).thenReturn(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 
like image 160
Jonny Heggheim Avatar answered Sep 18 '22 23:09

Jonny Heggheim


If you can use Maven as your build system, you can use a much simpler approach. Just define the same enum with an additional constant in your test classpath.

Let's say you have your enum declared under the sources directory (src/main/java) like this:

package my.package;  public enum MyEnum {     A,     B } 

Now you declare the exact same enum in the test sources directory (src/test/java) like this:

package my.package  public enum MyEnum {     A,     B,     C } 

The tests see the testclass path with the "overloaded" enum and you can test your code with the "C" enum constant. You should see your IllegalArgumentException then.

Tested under windows with maven 3.5.2, AdoptOpenJDK 11.0.3 and IntelliJ IDEA 2019.3.1

like image 37
Frank S. Avatar answered Sep 20 '22 23:09

Frank S.