2010-06-21 166 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()); 
}