2011-03-16 41 views
41

我有一個枚舉開關或多或少是這樣的:嘲諷的Java枚舉添加一個值來測試失敗的情況下

public static enum MyEnum {A, B} 

public int foo(MyEnum value) { 
    switch(value) { 
     case(A): return calculateSomething(); 
     case(B): return calculateSomethingElse(); 
    } 
    throw new IllegalArgumentException("Do not know how to handle " + value); 
} 

,我想有全部由測試所覆蓋的線路,但因爲代碼需要處理所有可能性,所以我不能在交換機中沒有相應的case語句的情況下提供一個值。

擴展枚舉以添加額外值是不可能的,只是嘲笑equals方法返回false將無法​​工作,因爲生成的字節碼使用幕後跳轉表來轉到正確的情況。所以我認爲PowerMock或許可以實現一些黑魔法。

謝謝!

編輯

至於我自己的枚舉,我以爲我可以只添加一個方法來值,從而避免了開關問題徹底;但我仍然留下這個問題,因爲它仍然很有趣。

+1

的IlegalArgument可從來沒有因爲明確的性能被拋出的枚舉,但是你有什麼要混淆你的代碼來測試它將處理不可能的事情?如果你真的想要拜拜你的生產線速度指標,爲什麼不刪除永遠不能執行的生產線呢? – Raedwald 2013-08-22 22:27:03

+5

@Raedwald理由2:首先,其他人可能會爲枚舉創建一個新值,並忘記爲該開關添加一個新案例;其次,在切換之後,如果沒有'throw'或'return',代碼將不會編譯。 – fortran 2013-08-23 10:17:22

+0

經過考慮,我認爲只是讓它沒有經過測試。沒有非法的枚舉值來觸發異常,並且嘲笑是很痛苦的。我認爲投球很好,這是未來的證明,只是很難測試。不值得費力測試,恕我直言。 – 2016-09-20 21:51:43

回答

-7

我把默認的情況下,用枚舉情況之一:

public static enum MyEnum {A, B} 

    public int foo(MyEnum value) { 
    if (value == null) throw new IllegalArgumentException("Do not know how to handle " + value); 

    switch(value) { 
     case(A): 
      return calculateSomething(); 
     case(B): 
     default: 
      return calculateSomethingElse(); 
    } 
    } 
+9

這不是所需的行爲,當添加新的枚舉值時它會產生意外的結果。 – fortran 2011-03-16 10:40:39

2

而不是使用一些過激的字節碼操作,以使測試打到最後一行foo,我會刪除它,並依賴於靜態代碼分析。例如,IntelliJ IDEA具有「Enum switch未執行大小寫」代碼檢查的聲明,如果缺少case,則會爲foo方法發出警告。

+1

這就是爲什麼我把失敗的行動之外的「默認」的情況下,允許靜態分析過......但我不願意刪除運行時檢查,只是依靠靜態的分析,我認爲兩者應該是相輔相成。 – fortran 2011-03-18 11:20:26

+0

這是我的觀點,使用靜態分析來補充測試套件+代碼覆蓋率。如果使用它,那麼帶'throw'語句的行會變得多餘並且可以被刪除,因爲IDE/build中會檢測到'switch'中缺少'case'。 – 2011-03-18 13:19:44

+0

我認爲這是一個好主意。枚舉的默認情況並不是真正的代碼,因爲它運行的應用程序的一部分,它的代碼將來可能會違反可能會引入錯誤的錯誤。最好只編寫你的應用程序,並確保它是正確的。 Sonar是否存在「未命中的枚舉switch語句」規則? – user2800708 2015-11-20 09:08:23

2

正如您在編輯中指出的那樣,您可以在枚舉本身中添加功能。但是,這可能不是最好的選擇,因爲它可能違反「一項責任」原則。實現此目的的另一種方法是創建一個靜態映射,其中包含枚舉值作爲鍵和作爲值的功能。通過這種方式,您可以通過循環遍歷所有值來輕鬆測試任何枚舉值是否具有有效行爲。這個示例可能有點牽強,但這是我經常用來映射資源ID枚舉值的技術。

38

這是一個完整的例子。

的代碼幾乎是像你原來的(只是簡化了更好的測試驗證):

public enum MyEnum {A, B} 

public class Bar { 

    public int foo(MyEnum value) { 
     switch (value) { 
      case A: return 1; 
      case B: return 2; 
     } 
     throw new IllegalArgumentException("Do not know how to handle " + value); 
    } 
} 

這裏是全代碼覆蓋率的單元測試,測試工作與Powermock(1.4.10)的Mockito( 1.8.5)和JUnit(4.8.2):

@RunWith(PowerMockRunner.class) 
public class BarTest { 

    private Bar bar; 

    @Before 
    public void createBar() { 
     bar = new Bar(); 
    } 

    @Test(expected = IllegalArgumentException.class) 
    @PrepareForTest(MyEnum.class) 
    public void unknownValueShouldThrowException() throws Exception { 
     MyEnum C = PowerMockito.mock(MyEnum.class); 
     Whitebox.setInternalState(C, "name", "C"); 
     Whitebox.setInternalState(C, "ordinal", 2); 

     PowerMockito.mockStatic(MyEnum.class); 
     PowerMockito.when(MyEnum.values()).thenReturn(new MyEnum[]{MyEnum.A, MyEnum.B, C}); 

     bar.foo(C); 
    } 

    @Test 
    public void AShouldReturn1() { 
     assertEquals(1, bar.foo(MyEnum.A)); 
    } 

    @Test 
    public void BShouldReturn2() { 
     assertEquals(2, bar.foo(MyEnum.B)); 
    } 
} 

結果:

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.628 sec 
+4

我跟着使用1.9.0的Mockito和PowerMock 1.4.12你的榜樣,我能夠在執行開關()語句java的拋出一個異常java.lang.ArrayIndexOutOfBounds喜歡它知道的代碼注入新的枚舉進入我的列表但是不應該有額外的一個。有什麼想法嗎? – Melloware 2012-07-20 22:01:35

+3

如果有人遇到@Melloware遇到的問題,以下內容可能會有用。爲了讓上面的例子能夠在我自己的測試中工作,我必須添加原始枚舉的所有值,並且將模擬的值添加到when/thenReturn語句中,並正確設置序號。如果你嘲笑一個額外的值,那麼序號應該是你原始的unmocked枚舉值的數量。 – JeroenHoek 2013-10-04 21:23:41

+2

你怎麼能PowerMockito.mockStatic(MyEnum.class);?它應該給java.lang.IllegalArgumentException異常:不能繼承final類 – Thamiar 2015-11-20 08:59:16

5

@Melloware

...代碼執行switch()語句的Java拋出一個java.lang.ArrayIndexOutOfBounds ...

我有同樣的問題。使用新的Enum作爲測試類中的第一個運行測試。我創建的錯誤這個問題:https://code.google.com/p/powermock/issues/detail?id=440

+2

當我使用@PrepareForTest MyEnum.class)在方法級別。 – 2013-05-22 23:38:11

1

JMock的(至少爲2.5.1,我使用的版本)可以做到這一點的開箱。您將需要設置您的Mockery以使用ClassImposterizer。

Mockery mockery = new Mockery(); 
mockery.setImposterizer(ClassImposterizer.INSTANCE); 
MyEnum unexpectedValue = mockery.mock(MyEnum.class); 
0

所有的Mockito首先可以創建可整數長等優點 它不能建立正確的枚舉的枚舉有序號名稱 值等的具體數目,所以如果我有一個枚舉模擬數據

public enum HttpMethod { 
     GET, POST, PUT, DELETE, HEAD, PATCH; 
} 

所以我有枚舉列舉HTTPMethod共5序,但不的Mockito不知道它.Mockito創建模擬數據和空所有的時間,你會在傳遞一個空值結束。 所以在這裏建議你隨機的順序,並獲得可以用於其他測試傳遞一個正確的枚舉的解決方案

import static org.mockito.Mockito.mock; 

import java.util.Random; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.Matchers; 
import org.mockito.internal.util.reflection.Whitebox; 
import org.powermock.api.mockito.PowerMockito; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 

import com.amazonaws.HttpMethod; 




//@Test(expected = {"LoadableBuilderTestGroup"}) 
//@RunWith(PowerMockRunner.class) 
public class testjava { 
    // private static final Class HttpMethod.getClass() = null; 
    private HttpMethod mockEnumerable; 

    @Test 
    public void setUpallpossible_value_of_enum() { 
     for (int i=0 ;i<10;i++){ 
      String name; 
      mockEnumerable= Matchers.any(HttpMethod.class); 
      if(mockEnumerable!= null){ 
       System.out.println(mockEnumerable.ordinal()); 
       System.out.println(mockEnumerable.name()); 

       System.out.println(mockEnumerable.name()+"mocking suceess"); 
      } 
      else { 
       //Randomize all possible value of enum 
       Random rand = new Random(); 
       int ordinal = rand.nextInt(HttpMethod.values().length); 
       // 0-9. mockEnumerable= 
       mockEnumerable= HttpMethod.values()[ordinal]; 
       System.out.println(mockEnumerable.ordinal()); 
       System.out.println(mockEnumerable.name()); 
      } 
     } 
    } 







    @Test 
    public void setUpallpossible_value_of_enumwithintany() { 
     for (int i=0 ;i<10;i++){ 
      String name; 
      mockEnumerable= Matchers.any(HttpMethod.class); 
      if(mockEnumerable!= null){ 
       System.out.println(mockEnumerable.ordinal()); 
       System.out.println(mockEnumerable.name()); 

       System.out.println(mockEnumerable.name()+"mocking suceess"); 
      } else { 
       int ordinal; 
       //Randomize all possible value of enum 
       Random rand = new Random(); 
       int imatch = Matchers.anyInt(); 
       if( imatch>HttpMethod.values().length) 
       ordinal = 0 ; 
       else 
       ordinal = rand.nextInt(HttpMethod.values().length); 

       // 0-9. mockEnumerable= 
       mockEnumerable= HttpMethod.values()[ordinal]; 
       System.out.println(mockEnumerable.ordinal()); 
       System.out.println(mockEnumerable.name());  
      } 
     } 
    } 
} 

輸出:

0 
GET 
0 
GET 
5 
PATCH 
5 
PATCH 
4 
HEAD 
5 
PATCH 
3 
DELETE 
0 
GET 
4 
HEAD 
2 
PUT