枚举值的单元测试不存在?

Fer*_*roc 14 java junit mockito

一些示例代码首先......

枚举:

public enum TestEnum {
   YES,
   NO
}
Run Code Online (Sandbox Code Playgroud)

一些代码:

public static boolean WorkTheEnum(TestEnum theEnum) {
   switch (theEnum) {
      case YES:
         return true;
      case NO:
         return false;
      default:
         // throws an exception here
   }
}
Run Code Online (Sandbox Code Playgroud)

问题:
TestEnum是我从不同开发人员的不同代码导入的东西.所以它实际上可以改变.对于这种情况,我想要一个实际检查该非现有值的单元测试.但我根本不知道如何用Mockito和JUnit做到这一点.

这部分当然不起作用:

@Test(expected=Exception.class)
public void DoesNotExist_throwsException() throws Exception {
    when(TestEnum.MAYBE).thenReturn(TestEnum.MAYBE);
    WorkTheEnum(TestEnum.MAYBE);
}
Run Code Online (Sandbox Code Playgroud)

我发现了一个使用PowerMock的例子,但我无法与Mockito合作.

有任何想法吗?

ass*_*ias 6

怎么样简单:

Set<String> expected = new HashSet<> (Arrays.asList("YES", "NO"));
Set<String> actual = new HashSet<>();
for (TestEnum e : TestEnum.values()) actual.add(e.name());
assertEquals(expected, actual);
Run Code Online (Sandbox Code Playgroud)

(使用HashSet而不是ArrayList,因为顺序无关紧要)


msa*_*ord 6

基于@assylias的答案,我认为这是你能做到的最好的:

List<String> unknown = new ArrayList<>();
for (TestEnum e : TestEnum.values())
  unknown.add(e.name());
unknown.removeAll(Arrays.asList("YES", "NO"));
if (unknown.isEmpty()) {
  // Not possible to reach default case, do whatever you need to do
} else {
  TestEnum notIncluded = TestEnum.valueOf(unknown.get(0));
  workTheEnum(notIncluded);
}
Run Code Online (Sandbox Code Playgroud)

由于switch语句的编译方式,不可能(AFAIK)伪造语句中不存在的enum值.即使您通过反射来摆弄实例中的内部字段,该陈述也将给出一个而不是落到案例中.switchenumordinalenumswitchArrayIndexOutOfBoundsExceptiondefault


以下是一些看似可能有用的代码,但由于ArrayIndexOutOfBoundsException上面提到的原因,它没有:

TestEnum abused = TestEnum.YES;
try {
  Class<?> c = abused.getClass().getSuperclass();
  Field[] declaredFields = c.getDeclaredFields();
  Field ordinalField = null;
  for (Field e : declaredFields) {
    if (e.getName().equals("ordinal")) {
      ordinalField = e;
    }
  }
  ordinalField.setAccessible(true);
  ordinalField.setInt(abused, TestEnum.values().length);
  workTheEnum(abused);
} catch (Exception e) {
  e.printStackTrace(System.err);
}
Run Code Online (Sandbox Code Playgroud)

好的,这可能适合你.它非常hacky,所以对我而言,可能比没有100%代码覆盖率,YMMV更糟糕.它的工作原理是将枚举序数查找数组替换为包含全零的数组,这些数组将落入默认情况.

// Setup values - needs to be called so that
// $SWITCH_TABLE$FooClass$BarEnum is initialised.
workTheEnum(TestEnum.YES);
workTheEnum(TestEnum.NO);

// This is the class with the switch statement in it.
Class<?> c = ClassWithSwitchStatement.class;

// Find and change fields.
Map<Field, int[]> changedFields = new HashMap<>();
Field[] declaredFields = c.getDeclaredFields();
try {
  for (Field f : declaredFields) {
    if (f.getName().startsWith("$SWITCH_TABLE$")) {
      f.setAccessible(true);
      int[] table = (int[])f.get(null);
      f.set(null, new int[table.length]);
      changedFields.put(f, table);
    }
  }
  workTheEnum(TestEnum.YES);
} finally {
  for (Map.Entry<Field, int[]> entry : changedFields.entrySet()) {
    try {
      entry.getKey().set(null, entry.getValue());
    } catch (Exception ex) {
      ex.printStackTrace(System.err);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)