在Junit中处理异常的方式有很多种,比如:

3 ways of handling exceptions in JUnit. Which one to choose?
JUnit ExpectedException rule: beyond basics

有人推荐我尝试一下catch-exception,在这篇文章中我将会向大家介绍。简而言之,catch-exception库可以仅在一行代码中捕获异常,以后再对其进行处理。

通过Maven安装

为了快速进入正题,我使用了一个包含有一系列依赖关系JUnit, Mocito, Hamcrest, AssertJUnit Testing Demo项目作为示例,并添加了catch-exception

添加catch-exception

Java

1

2

3

4

5

6

<dependency>

    <groupId>com.googlecode.catch-exception</groupId>

    <artifactId>catch-exception</artifactId>

    <version>1.2.0</version>

    <scope>test</scope>

</dependency>

依存关系树如下:

依存关系树

1

2

3

4

5

6

7

8

9

10

11

12

13

[INFO] --- maven-dependency-plugin:2.1:tree @ unit-testing-demo ---

[INFO] com.github.kolorobot:unit-testing-demo:jar:1.0.0-SNAPSHOT

[INFO] +- org.slf4j:slf4j-api:jar:1.5.10:compile

[INFO] +- org.slf4j:jcl-over-slf4j:jar:1.5.10:runtime

[INFO] +- org.slf4j:slf4j-log4j12:jar:1.5.10:runtime

[INFO] +- log4j:log4j:jar:1.2.15:runtime

[INFO] +- junit:junit:jar:4.11:test

[INFO] +- org.mockito:mockito-core:jar:1.9.5:test

[INFO] +- org.assertj:assertj-core:jar:1.5.0:test

[INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test

[INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test

[INFO] +- org.objenesis:objenesis:jar:1.3:test

[INFO] - com.googlecode.catch-exception:catch-exception:jar:1.2.0:test

准备开始

被测系统(SUT):

被测试系统(SUT)

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class ExceptionThrower {

    void someMethod() {

        throw new RuntimeException("Runtime exception occurred");

    }

    void someOtherMethod() {

        throw new RuntimeException("Runtime exception occurred",

            new IllegalStateException("Illegal state"));

    }

    void yetAnotherMethod(int code) {

        throw new CustomException(code);

    }

}

使用AssertJ断言的BDD-style方法catch-exception示例:

BDD-style方法catch-exception示例

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import org.junit.Test;

import static com.googlecode.catchexception.CatchException.*;

import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*;

public class CatchExceptionsTest {

    @Test

    public void verifiesTypeAndMessage() {

        when(new SomeClass()).someMethod();

        then(caughtException())

        .isInstanceOf(RuntimeException.class)

        .hasMessage("Runtime exception occurred")

        .hasMessageStartingWith("Runtime")

        .hasMessageEndingWith("occured")

        .hasMessageContaining("exception")

        .hasNoCause();              

    }

}

看起来不错——简单,可读性高。没有JUnit运行。请注意,我指定的那个在SomeClass类中抛出异常的方法。可想而知,我可以在一个测试中检验多个异常。但是这违背了测试中的单一任务原则,所以不推荐这种做法。另外,如果你是用Eclipse工作的话,也许这篇文章对你有用。

检查异常的原因

我相信下面的代码就没有必要讨论了吧:

Test

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import org.junit.Test;

import static com.googlecode.catchexception.CatchException.*;

import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*;

public class CatchExceptionsTest {

    @Test

    public void verifiesCauseType() {

        when(new ExceptionThrower()).someOtherMethod();

        then(caughtException())

        .isInstanceOf(RuntimeException.class)

        .hasMessage("Runtime exception occurred")

        .hasCauseExactlyInstanceOf(IllegalStateException.class)

        .hasRootCauseExactlyInstanceOf(IllegalStateException.class);

    }

}

使用Hamcrest来检查自定义异常

为了检查自定义异常,我用了在之前的文章中谈到的Hamcrest匹配代码。

Hamcrest

Java

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

36

37

class CustomException extends RuntimeException {

    private final int code;

    public CustomException(int code) {

        this.code = code;

    }

    public int getCode() {

        return code;

    }

}

class ExceptionCodeMatches extends TypeSafeMatcher<CustomException> {

    private int expectedCode;

    public ExceptionCodeMatches(int expectedCode) {

        this.expectedCode = expectedCode;

    }

    @Override

    protected boolean matchesSafely(CustomException item) {

        return item.getCode() == expectedCode;

    }

    @Override

    public void describeTo(Description description) {

        description.appendText("expects code ")

        .appendValue(expectedCode);

    }

    @Override

    protected void describeMismatchSafely(CustomException item, Description mismatchDescription) {

        mismatchDescription.appendText("was ")

        .appendValue(item.getCode());

    }

}

测试部分:

Test2

Java

1

2

3

4

5

6

7

8

9

10

11

import org.junit.Test;

import static com.googlecode.catchexception.CatchException.*;

import static org.junit.Assert.*;

public class CatchExceptionsTest {

    @Test

    public void verifiesCustomException() {

        catchException(new ExceptionThrower(), CustomException.class).yetAnotherMethod(500);

        assertThat((CustomException) caughtException(), new ExceptionCodeMatcher(500));

总结

catch-exception看起来很棒,上手非常简单。比起在JUnit中的方法它具有很多优势。如果有机会我竟会深入的研究一下这个库,希望在现实中能有一个这样的机会。

如果你感兴趣,可以看一下我其他的文章:

英文原文由Rafal Borowiec发表在javacodegeeks赖信涛翻译,Importnew校对。