java 不识别enum_关于java:Unit Test for Enum值不存在?
首先是一些示例代碼...
枚舉:
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一起使用。
有任何想法嗎?
我沒有足夠的信心聲稱這是答案,但是:是否不可能做TestEnum notAValidEnum = Mockito.mock(TestEnum.class);,然后將其提供給WorkTheEnum() ...?
可能重復:stackoverflow.com/questions/5323505
可能相關:stackoverflow.com/questions/28013717/
以@assylias的答案為基礎,我認為這是您可以做的最好的事情:
List 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語句的編譯方式,不可能(AFAIK)偽造switch語句中不存在的enum值。即使您通過反射擺弄enum實例中的內部ordinal字段,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 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 entry : changedFields.entrySet()) {
try {
entry.getKey().set(null, entry.getValue());
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
}
也許嘲笑不是正確的方法嗎?但是必須有一種使用反射添加值的方法,以便枚舉實際上是有效的并且具有所有這些值!
不,我不這么認為。編譯switch語句時,將生成一個數組,其中包含該時間點存在的枚舉值的所有序號。如果嘗試修改現有值以更改其序數,則編譯器生成的代碼將訪問數組范圍之外的值,并失敗并顯示ArrayIndexOutOfBoundsException。糟糕的是附加了一些執行此操作的代碼,以便您可以查看是否喜歡。
感謝您詳細的答復@msandiford。我想運氣不好。
對我來說,它僅在將f.setAccessible(true);添加到循環中時才有效(請參閱我的編輯)。這可能與Java8有關嗎?
另一個注意事項:對我來說,它不適用于Maven(->在此排除它)。一旦Maven使用開關編譯了該類,就必須再次在Eclipse中編譯該類以使測試再次在Eclipse中工作。換句話說,無論測試是否有效,編譯器都起著重要的作用。
一個簡單的怎么樣:
Set expected = new HashSet<> (Arrays.asList("YES","NO"));
Set actual = new HashSet<>();
for (TestEnum e : TestEnum.values()) actual.add(e.name());
assertEquals(expected, actual);
(使用HashSet而不是ArrayList,因為順序無關緊要)
但這只會測試枚舉的內容,而不會測試switch語句的默認路徑。
我明白了-如果要達到默認大小寫,您想確保該方法引發異常,因此您想模擬枚舉以模擬非預期值。那正確嗎?
那正是我想做的,是的。
@Feroc的問題是您無法通過反射實例化枚舉的新實例,因此我不確定mockito是否可以解決該限制...
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;
}
謝謝你的幫助。不太確定我們是否可以在此處使用Powermock。
它只是另一個mock庫。無論如何,這取決于選擇。
如果您將代碼轉換為OP問題,則可以改善答案
@VinayVeluri:PowerMock不僅僅是另一個模擬庫。它比Mockito更強大,這就是為什么有些人不想在項目中使用它的原因。它用于測試遺留代碼,例如靜態方法無法通過Mockito等"常規"模擬庫進行測試。只要您開發一個新項目,就應該以不需要PowerMock的方式進行開發,但是Mockito就足夠了。
借助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);
}
}
}
您可以嘲笑,修改或嘗試使其工作,但是有一種非常簡單的方法來做到這一點。我假設您正在使用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
}
現在您可以在測試中使用新值INVALID_FOR_TEST_ONLY,并且在產品概要文件中將不可用。
有兩個缺點:
如果您更新產品枚舉,則可能還需要更新測試(如果要進行測試,則需要更新)
某些IDE可能無法正確使用此技巧,即使Maven很好理解它
總結
以上是生活随笔為你收集整理的java 不识别enum_关于java:Unit Test for Enum值不存在?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java filereader 示例_J
- 下一篇: 自动换挡锁定灯是黄色的怎么回事啊?