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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25929351/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 08:58:07  来源:igfitidea点击:

Unit Test for Enum value that doesn't exist?

javajunitmockito

提问by Feroc

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.

问题:
TestEnum 是我从不同开发人员的不同代码中导入的东西。所以它实际上可以改变。对于这种情况,我想要一个单元测试来实际检查那个不存在的值。但我根本不知道如何用 Mockito 和 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.

我找到了一个使用 PowerMock 的例子,但我无法让它与 Mockito 一起工作。

Any ideas?

有任何想法吗?

回答by assylias

How about a simple:

一个简单的怎么样:

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

(using HashSet rather than ArrayList because order does not matter)

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

回答by clstrfsck

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

基于@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);
}

It isn't possible (AFAIK) to fake a non-existent enumvalue in a switchstatement, due to the way that enumswitch statements are compiled. Even if you resort to fiddling with the internal ordinalfield in the enuminstance via reflection, the switchstatement will give an ArrayIndexOutOfBoundsExceptionrather than falling through to the defaultcase.

由于switch 语句的编译方式,不可能 (AFAIK)enumswitch语句中伪造一个不存在的值enum。即使您通过反射来摆弄实例中的内部ordinal字段enum,该switch语句也会给出一个ArrayIndexOutOfBoundsException而不是落入default案例中。



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

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


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.

好的,这里有一些可能对你有用的东西。这很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);
    }
  }
}

回答by VinayVeluri

Mockitodoesn't support mocking of enum values but powermockdoes.

Mockito不支持模拟枚举值,但支持powermock

Try this.

试试这个。

I have created my own classes to simulate them. Please map to your own classes.

我创建了自己的类来模拟它们。请映射到您自己的类。

@RunWith(PowerMockRunner.class)
@PrepareForTest(Trail.class)
public class TrailTest {
    @Before
    public void setUp() {
        Trail mockTrail = PowerMock.createMock(Trail.class);
        Whitebox.setInternalState(mockTrail, "name", "Default");
        Whitebox.setInternalState(mockTrail, "ordinal", 2);
        PowerMock.mockStatic(Trail.class);
        expect(Trail.values()).andReturn(new Trail[]{Trail.YES, Trail.NO, mockTrail});
        expect(Trail.valueOf("default value")).andReturn(mockTrail);
        PowerMock.replay(Trail.class);
    }

    @Test(expected = RuntimeException.class)
    public void test() {
        Trail aDefault = Trail.valueOf("default value");
        BasicTrails.find(aDefault);
    }
}

This is the method :

这是方法:

public class BasicTrails {

public static boolean find(Trail trail) {
    switch (trail) {
        case YES:
            return true;
        case NO:
            return false;
        default:
            throw new RuntimeException("Invalid");
    }
}

This is the enum

这是枚举

public enum Trail {
    YES, NO;
}

回答by Swapnil

With the help of Powermock we can achieve this as Powermock supports mocking of final classes

在 Powermock 的帮助下,我们可以实现这一点,因为 Powermock 支持模拟最终类

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.BDDMockito;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Trail.class)

public class TrailTest {

    @Mock Trail mockTrail;

    @Before
    public void setUp() {
        PowerMockito.mockStatic(Trail.class);
        BDDMockito.given(Trail.values()).willReturn(new Trail[]{Trail.YES, Trail.NO, mockTrail});
        BDDMockito.given(Trail.valueOf("YES")).willReturn(mockTrail.YES);
        BDDMockito.given(Trail.valueOf("NO")).willReturn(mockTrail.NO);

    }

    @Test
    public void test() {

        assertTrue(BasicTrails.find(mockTrail.valueOf("YES")));

        assertFalse(BasicTrails.find(mockTrail.valueOf("NO")));

        try{
             Trail aDefault = mockTrail.valueOf("default value");
        }catch (Exception e) {
            System.out.println(e);
        }


    }
}

回答by Damian

You can mock, hack or trying to make it work but there is quite simple way how to do this. I assume that you are working with maven or gradle so you have mainand testprofiles.

您可以模拟、破解或尝试使其工作,但有很简单的方法可以做到这一点。我假设您正在使用 maven 或 gradle,因此您拥有maintest配置文件。

Then in main profile you have code as above:

然后在主配置文件中,您有如上的代码:

package my.cool.package;

public enum TestEnum {
    YES,
    NO
}

but then in test profile you can have another one:

但是在测试配置文件中你可以有另一个:

// EXACTLY SAME as above
package my.cool.package;

public enum TestEnum {
    YES,
    NO,
    INVALID_FOR_TEST_ONLY
}

and now you can use new value INVALID_FOR_TEST_ONLYin testand it won't be available in prod profile.

现在您可以INVALID_FOR_TEST_ONLY测试中使用新值,并且在 prod 配置文件中不可用。

There are two disadvantages:

有两个缺点:

  • if you update prod enum you may need to update test as well (if you want test then to)
  • some IDE may not work with this trick properly even maven understands it well
  • 如果您更新 prod 枚举,您可能还需要更新测试(如果您想要测试)
  • 某些 IDE 可能无法正确使用此技巧,即使 Maven 很了解它