嘲笑Java枚举添加一个值来testing失败的情况
我有一个枚举开关或多或less像这样:
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); }
我想要testing覆盖所有的行,但是由于代码需要处理所有的可能性,所以我不能在交换机中没有相应的case语句的情况下提供一个值。
扩展枚举以添加额外的值是不可能的,只是嘲笑equals方法返回false
将不起作用,或者是因为生成的字节码使用幕后的跳转表来转到正确的情况下…所以,我已经认为PowerMock或许可以实现一些黑魔法。
谢谢!
编辑 :
由于我拥有枚举,我以为我可以添加一个方法的值,从而完全避免切换问题; 但是我仍然留下这个问题,因为它仍然很有趣。
Java专家通讯( http://www.javaspecialists.eu/archive/Issue161.html )存在一个试图解决这类问题的问题。 它涉及到SUN内部类的各种reflection魔术和用法。 我承认我没有完全阅读,但也许是你感兴趣?
这是一个完整的例子。
代码几乎就像你原来的(只是简化了更好的testingvalidation):
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); } }
这里是完整代码覆盖的unit testing,testing适用于Powermock(1.4.10),Mockito(1.8.5)和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)); } }
结果:
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.628 sec
@Melloware
执行switch()语句的代码java抛出一个java.lang.ArrayIndexOutOfBounds …
我有着同样的问题。 用新的Enum作为testing类中的第一个运行testing。 我用这个问题创build了一个bug: https : //code.google.com/p/powermock/issues/detail?id = 440
而不是使用一些激进的字节码操作来启用testing,以便在foo
的最后一行,我将删除它,而不是依靠静态代码分析。 例如,IntelliJ IDEA具有“未命中的Enum switch
语句”代码检查,如果缺lesscase
,将会为foo
方法发出警告。
正如你在编辑中指出的那样,你可以在enum本身添加function。 但是,这可能不是最好的select,因为它可能违反“一个责任”的原则。 另一种方法是创build一个静态映射,它包含枚举值作为键和作为值的function。 这样,您可以轻松地testing任何枚举值是否具有循环所有值的有效行为。 这个例子可能有点牵强,但这是我经常用来将资源ID映射到枚举值的技术。
首先Mockito可以创build模拟数据,可以是整数长等。它不能创build正确的枚举,因为枚举具有特定的序数名称值等,所以如果我有一个枚举
public enum HttpMethod { GET, POST, PUT, DELETE, HEAD, PATCH; }
所以我有枚举HttpMethod共5顺序,但mockito不知道它.Mockito创build模拟数据和其所有的时间,你会最终传递一个空值。 所以这里提出的解决scheme,你随机sorting和获得一个可以通过其他testing的权利枚举
import static org.mockito.Mockito.mock; import java.util.Random; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Matchers; import org.mockito.internal.util.reflection.Whitebox; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.amazonaws.HttpMethod; //@Test(expected = {"LoadableBuilderTestGroup"}) //@RunWith(PowerMockRunner.class) public class testjava { // private static final Class HttpMethod.getClass() = null; private HttpMethod mockEnumerable; @Test public void setUpallpossible_value_of_enum () { for ( int i=0 ;i<10;i++){ String name; mockEnumerable= Matchers.any(HttpMethod.class); if(mockEnumerable!= null){ System.out.println(mockEnumerable.ordinal()); System.out.println(mockEnumerable.name()); System.out.println(mockEnumerable.name()+"mocking suceess"); } else { //Randomize all possible value of enum Random rand = new Random(); int ordinal = rand.nextInt(HttpMethod.values().length); // 0-9. mockEnumerable= mockEnumerable= HttpMethod.values()[ordinal]; System.out.println(mockEnumerable.ordinal()); System.out.println(mockEnumerable.name()); } } } @Test public void setUpallpossible_value_of_enumwithintany () { for ( int i=0 ;i<10;i++){ String name; mockEnumerable= Matchers.any(HttpMethod.class); if(mockEnumerable!= null){ System.out.println(mockEnumerable.ordinal()); System.out.println(mockEnumerable.name()); System.out.println(mockEnumerable.name()+"mocking suceess"); } else { int ordinal; //Randomize all possible value of enum Random rand = new Random(); int imatch = Matchers.anyInt(); if( imatch>HttpMethod.values().length) ordinal = 0 ; else ordinal = rand.nextInt(HttpMethod.values().length); // 0-9. mockEnumerable= mockEnumerable= HttpMethod.values()[ordinal]; System.out.println(mockEnumerable.ordinal()); System.out.println(mockEnumerable.name()); } } } }
输出:
0 GET 0 GET 5 PATCH 5 PATCH 4 HEAD 5 PATCH 3 DELETE 0 GET 4 HEAD 2 PUT
jMock(至less从我使用的版本2.5.1开始)可以做到这一点。 您将需要设置您的Mockery使用ClassImposterizer。
Mockery mockery = new Mockery(); mockery.setImposterizer(ClassImposterizer.INSTANCE); MyEnum unexpectedValue = mockery.mock(MyEnum.class);
我会把默认情况下与枚举的情况之一:
public static enum MyEnum {A, B} public int foo(MyEnum value) { if (value == null) throw new IllegalArgumentException("Do not know how to handle " + value); switch(value) { case(A): return calculateSomething(); case(B): default: return calculateSomethingElse(); } }