首先是一些示例代码......
枚举:
public enum TestEnum {
YES,
NO
}
一些代码:
public static boolean WorkTheEnum(TestEnum theEnum) {
switch (theEnum) {
case YES:
return true;
case NO:
return false;
default:
// throws an exception here
}
}
问题:
TestEnum是我从不同开发人员的不同代码导入的东西。所以它实际上可以改变。对于这种情况,我想要一个实际检查该非现有值的单元测试。但我根本不知道如何使用Mockito和JUnit。
这部分当然不起作用:
@Test(expected=Exception.class)
public void DoesNotExist_throwsException() throws Exception {
when(TestEnum.MAYBE).thenReturn(TestEnum.MAYBE);
WorkTheEnum(TestEnum.MAYBE);
}
我找到了一个使用PowerMock的例子,但我无法与Mockito合作。
有什么想法吗?
答案 0 :(得分: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);
(使用HashSet而不是ArrayList,因为顺序无关紧要)
答案 1 :(得分: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);
}
由于enum
转换语句的编译方式,在switch
语句中伪造不存在的enum
值是不可能的(AFAIK)。即使您通过反射摆弄ordinal
实例中的内部enum
字段,switch
语句也会提供ArrayIndexOutOfBoundsException
而不是default
案件。
以下是一些看起来可行的代码,但由于上面提到的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);
}
好的,这可能适合你。它非常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);
}
}
}
答案 2 :(得分:4)
Mockito
不支持模拟枚举值,但powermock
会这样做。
试试这个。
我创建了自己的类来模拟它们。请映射到您自己的课程。
@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);
}
}
这是方法:
public class BasicTrails {
public static boolean find(Trail trail) {
switch (trail) {
case YES:
return true;
case NO:
return false;
default:
throw new RuntimeException("Invalid");
}
}
这是枚举
public enum Trail {
YES, NO;
}
答案 3 :(得分:1)
在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);
}
}
}
答案 4 :(得分:0)
您可以模拟,破解或尝试使其工作,但有很简单的方法如何做到这一点。我假设您正在使用maven或gradle,因此您拥有main
和test
个人资料。
然后在主要配置文件中,您拥有上述代码:
package my.cool.package;
public enum TestEnum {
YES,
NO
}
但是在测试档案中你可以有另一个:
// EXACTLY SAME as above
package my.cool.package;
public enum TestEnum {
YES,
NO,
INVALID_FOR_TEST_ONLY
}
现在您可以在test中使用新值INVALID_FOR_TEST_ONLY
,但它无法在prod配置文件中使用。
有两个缺点: