Enum值的单元测试不存在? [英] Unit Test for Enum value that doesn't exist?

查看:189
本文介绍了Enum值的单元测试不存在?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



枚举:

  public enum TestEnum {
YES,
NO
}

一些代码:

  public static boolean WorkTheEnum(TestEnum theEnum){
switch(theEnum){
case YES:
返回true;
案例NO:
返回false;
默认值:
//在此处抛出异常
}
}

问题:

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



这部分当然不起作用:

  @Test(expected = Exception.class)
public void DoesNotExist_throwsException()抛出异常{
when(TestEnum.MAYBE).thenReturn(TestEnum.MAYBE) );
WorkTheEnum(TestEnum.MAYBE);
}

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



任何想法?

解决方案

建立在来自@assylias的回答,我认为这是你能做的最好的事情:

  List< String> unknown = new ArrayList<>(); 
for(TestEnum e:TestEnum.values())
unknown.add(e.name());
unknown.removeAll(Arrays.asList(YES,NO));
if(unknown.isEmpty()){
//无法达到默认情况,做任何你需要做的事情
}否则{
TestEnum notIncluded = TestEnum.valueOf( unknown.get(0));
workTheEnum(notIncluded);
}

不可能(AFAIK)伪造不存在的 enum 开关语句中的值,因为 enum 的方式switch语句被编译。即使您通过反射来摆弄枚举实例中的内部序数字段, switch 语句将给出 ArrayIndexOutOfBoundsException ,而不是落到默认的情况。 / p>




这是一些看起来可能有用的代码,但由于而没有上面提到的ArrayIndexOutOfBoundsException

  TestEnum abused = TestEnum.YES; 
试试{
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(滥用,TestEnum.values()。length);
workTheEnum(滥用);
} catch(例外e){
e.printStackTrace(System.err);
}






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

  //设置值 - 需要调用,以便初始化
// $ SWITCH_TABLE $ FooClass $ BarEnum。
workTheEnum(TestEnum.YES);
workTheEnum(TestEnum.NO);

//这是包含switch语句的类。
Class<?> c = ClassWithSwitchStatement.class;

//查找和更改字段。
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);
}最后{
for(Map.Entry< Field,int []> entry:changedFields.entrySet()){
try {
entry.getKey()。set (null,entry.getValue());
} catch(Exception ex){
ex.printStackTrace(System.err);
}
}
}


Some example code first...

The enum:

public enum TestEnum {
   YES,
   NO
}

Some code:

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

Problem:
The TestEnum is something I import from a different code of a different developer. So it actually could change. For this case I want to have a unit test that actually checks for that non existing value. But I simply don't know how to do it with Mockito and JUnit.

This part is of course not working:

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

I found one example that usees PowerMock, but I couldn't get it to work with Mockito.

Any ideas?

解决方案

Building on the answer from @assylias, I think this is the best you can do:

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);
}

It isn't possible (AFAIK) to fake a non-existent enum value in a switch statement, due to the way that enum switch statements are compiled. Even if you resort to fiddling with the internal ordinal field in the enum instance via reflection, the switch statement will give an ArrayIndexOutOfBoundsException rather than falling through to the default case.


Here is some code that looks like it might work, but doesn't, due to the ArrayIndexOutOfBoundsException mentioned above:

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);
}


OK, here is something that might work for you. It's pretty hacky, so to me it's probably worse than not having 100% code coverage, YMMV. It works by replacing the enum ordinal lookup arrays with arrays containing all zeros, which falls through to the default case.

// 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);
    }
  }
}

这篇关于Enum值的单元测试不存在?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆