2010-06-21 169 views
15

編輯:目前不提供JUnit 4。JUnit異常測試

那裏嗨,

我有一個關於使用JUnit「智能」異常測試問題。在這個時候,我不喜歡這樣寫道:

public void testGet() { 

    SoundFileManager sfm = new SoundFileManager(); 

     // Test adding a sound file and then getting it by id and name. 
     try { 
      SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
      SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
      assertTrue(sf!=null); 
      System.out.println(sf.toString()); 

      sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
      assertTrue(sf!=null); 
      System.out.println(sf.toString()); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

     // Test get with invalid id. 
     try { 
      sfm.getSoundfile(-100); 
      fail("Should have raised a RapsManagerException"); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

     // Test get by name with invalid name 
     try { 
      sfm.getSoundfileByName(new String()); 
      fail("Should have raised a RapsManagerException"); 
     } catch (RapsManagerException e) { 
      System.out.println(e.getMessage()); 
     } 

    } 

正如你所看到的,我需要一個try/catch塊的是應該拋出一個異常,各項功能。這似乎不是一個好方法 - 或者是否不可能減少try/catch的使用?

+0

我會建議反對這樣做的System.out.println內部測試。 System.out和System.err傾向於混淆併產生亂碼測試輸出。 – RockyMM 2012-11-20 12:31:59

回答

31

我建議你需要將testGet分成多個單獨的測試。個別try/catch塊似乎是相互獨立的。您可能還想將通用初始化邏輯提取到其自己的設置方法中。

一旦你有,你可以使用JUnit4的異常註解的支持,這樣的事情:

public class MyTest { 

private SoundManager sfm; 

@Before 
public void setup() { 
     sfm = new SoundFileManager(); 
} 

@Test 
public void getByIdAndName() { 
    // Test adding a sound file and then getting it by id and name. 
     SoundFile addedFile =    
     sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 

     sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 
} 

@Test(expected=RapsManagerException.class) 
public void getByInvalidId() { 
     // Test get with invalid id. 
     sfm.getSoundfile(-100); 
} 

@Test(expected=RapsManagerException.class) 
public void getByInvalidName() { 
     // Test get with invalid id. 
     sfm.getSoundfileByName(new String()); 
} 
} 
+0

我喜歡這個,但我害怕在運行的項目中切換到JUnit4。 – InsertNickHere 2010-06-21 09:01:13

+4

@InsertNickHere:您的謹慎是值得讚揚的,但我建議在這種情況下錯位。 JUnit4可以在不改變的情況下運行JUnit3風格的測試,從而允許您逐次遷移一個測試。另外,JUnit4現在已經4歲了,真的是你感動了。 – skaffman 2010-06-21 09:29:46

+1

我必須完全同意你 - 在這種情況下,我必須將其移至JUnit 4. – InsertNickHere 2010-06-21 09:35:55

3

使用JUnit 4,您可以改爲使用註釋。但是,您應該將測試分爲3種不同的方法,以便乾淨地工作。請注意,恕我直言,在第一種情況下捕捉異常應該是失敗的,所以我相應地修改了catch塊。

public void testGet() { 
    SoundFileManager sfm = new SoundFileManager(); 

    // Test adding a sound file and then getting it by id and name. 
    try { 
     SoundFile addedFile = sfm.addSoundfile("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     SoundFile sf = sfm.getSoundfile(addedFile.getID()); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 

     sf = sfm.getSoundfileByName("E:\\Eclipse_Prj\\pSound\\data\\Adrenaline01.wav"); 
     assertTrue(sf!=null); 
     System.out.println(sf.toString()); 
    } catch (RapsManagerException e) { 
     fail(e.getMessage()); 
    } 
} 

@Test(expected=RapsManagerException.class) 
public void testGetWithInvalidId() { 
    SoundFileManager sfm = new SoundFileManager(); 

    sfm.getSoundfile(-100); 
} 

@Test(expected=RapsManagerException.class) 
public void testGetWithInvalidName() { 
    SoundFileManager sfm = new SoundFileManager(); 

    sfm.getSoundfileByName(new String()); 
} 
+0

正如我上面所說的,我喜歡這個主意,但我不願意在正在運行的項目中切換到JUnit4。 – InsertNickHere 2010-06-21 09:02:04

+0

@InsertNickHere,無論如何,你應該把你的測試分解成3個獨立的方法,通常的設置。那麼如果你真的想最小化異常處理代碼(並且你有很多重複的代碼就像你展示的那樣),你可以將try/catch提取到一個單獨的方法中,並通過一個接口傳遞實際的方法進行測試(但這真是矯枉過正恕我直言,它使你的測試代碼更難理解)。 – 2010-06-21 09:08:06

12

如果你有一個預期的異常,你不能使用註釋來捕獲它,你需要抓住它,並斷言,你已經得到了你所期望的。例如:

Throwable caught = null; 
try { 
    somethingThatThrows(); 
} catch (Throwable t) { 
    caught = t; 
} 
assertNotNull(caught); 
assertSame(FooException.class, caught.getClass()); 

如果您可以使用一個註釋來代替它,那麼它會更清晰。但是這並不總是可行的(例如,因爲您正在測試一系列方法或者因爲您在使用JUnit 3)。

+0

對不起,但我沒有看到你的版本相比我的任何優勢。也許沒有JUnit3 :( – InsertNickHere 2010-06-21 09:03:45

+1

)你在測試用例中放置太多,你在'try'中放置太多了,你正在打印消息來手動讀取,而不是讓測試斷言失敗!(您的代碼中嵌入了非便攜式路徑,但這是您的業務......) – 2010-06-21 09:15:18

+0

路徑僅用於此示例,我不會在此處發佈100%的原始代碼。不過謝謝你的建議。 :) – InsertNickHere 2010-06-21 09:35:03

2

最簡潔的語法是catch-exception提供:

public void testGet() { 
    SoundFileManager sfm = new SoundFileManager(); 
    ... // setup sound file manager 

    verifyException(sfm, RapsManagerException.class) 
     .getSoundfile(-100); 

    verifyException(sfm, RapsManagerException.class) 
     .getSoundfileByName(new String()); 
} 
0

在Java 8中,您可以使用lambda表達式可以更緊密地控制拋出異常的時間。如果你使用annotations方法,那麼你只是斷言異常是在測試方法的某處引發的。如果您在測試中執行多行代碼,那麼當您的測試失敗時就會冒風險。 Java 8解決方案就像這樣。

static void <T extends Exception> expectException(Class<T> type, Runnable runnable) { 
    try { 
     runnable.run() 
    } catch (Exception ex) { 
     assertTrue(ex.getClass().equals(type)); 
     return; 
    } 
    assertTrue(false); 
} 

用法:

@Test 
public void test() 
    MyClass foo = new MyClass(); 
    // other setup code here .... 
    expectException(MyException.class,() -> foo.bar()); 
}