关于java:JUnit异常测试

JUnit Exception Testing

编辑:目前不提供JUnit 4。

嗨,您好,

我有一个关于使用JUnit进行"智能"异常测试的问题。 这时,我这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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的使用?


我建议您需要将testGet分解为多个单独的测试。各个try / catch块似乎彼此非常独立。您可能还希望将公共初始化逻辑提取到其自己的设置方法中。

完成后,您可以使用JUnit4的异常注释支持,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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());
}
}


如果你有一个预期的异常并且你不能使用注释来捕获它,你需要捕获它并断言你已经得到了你所期望的。例如:

1
2
3
4
5
6
7
8
Throwable caught = null;
try {
   somethingThatThrows();
} catch (Throwable t) {
   caught = t;
}
assertNotNull(caught);
assertSame(FooException.class, caught.getClass());

如果您可以使用注释,请执行此操作,因为它更清晰。但这并不总是可行的(例如,因为您正在测试一系列方法或因为您正在使用JUnit 3)。


使用JUnit 4,您可以使用注释。但是,您应该将测试分为3种不同的方法,以便干净地工作。请注意,在第一个场景中捕获异常的IMHO应该是失败的,因此我相应地修改了catch块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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());
}


catch-exception提供了最简洁的语法:

1
2
3
4
5
6
7
8
9
10
public void testGet() {
    SoundFileManager sfm = new SoundFileManager();
    ... // setup sound file manager

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

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

在Java 8中,您可以使用lambda表达式来更严格地控??制何时抛出异常。如果您使用注释方法,那么您只是断言在测试方法中的某处抛出了异常。如果您在测试中执行了多行代码,那么当测试失败时,您将面临测试通过的风险。 Java 8解决方案就是这样的。

1
2
3
4
5
6
7
8
9
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);
}

用法:

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