在JUnit中处理异常的3种方法。 选择哪一个?

在JUnit中,有3种流行的方式来处理测试代码中的异常:

  • 尝试捕捉习语
  • 使用JUnit规则
  • 带注解

我们应该使用哪一个?何时使用?

尝试捕捉习语

这个习语是最受欢迎的习语之一,因为它已在JUnit 3中使用。

@Test
    public void throwsExceptionWhenNegativeNumbersAreGiven() {
        try {
            calculator.add("-1,-2,3");
            fail("Should throw an exception if one or more of given numbers are negative");
        } catch (Exception e) {
            assertThat(e)
                    .isInstanceOf(IllegalArgumentException.class)
                    .hasMessage("negatives not allowed: [-1, -2]");
        }
    }

上面的方法是一种常见的模式。 当没有引发异常并且在catch子句中验证了异常本身时,测试将失败(在上面的示例中,我使用了FEST Fluent断言),尽管它很好,但我更喜欢使用
ExpectedException规则。

使用JUnit规则

可以使用创建相同的示例
ExceptedException规则。 规则必须是标有@Rule批注的公共字段。 请注意,“抛出”规则可能会在许多测试中重复使用。

@Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void throwsExceptionWhenNegativeNumbersAreGiven() {
        // arrange
        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage(equalTo("negatives not allowed: [-1, -2]"));
        // act
        calculator.add("-1,-2,3");
    }

通常,我发现上面的代码更具可读性,因此我在项目中使用了这种方法。

当未引发异常时,您将收到以下消息: java.lang.AssertionError:预期引发的测试(java.lang.IllegalArgumentException的实例和带有消息“不允许负数的异常:[-1,-2]” ) 。 挺好的。

但并非所有例外情况我都可以通过上述方法进行检查。 有时我只需要检查抛出的异常的类型,然后使用@Test批注。

带注解

@Test (expected = IllegalArgumentException.class)
    public void throwsExceptionWhenNegativeNumbersAreGiven() {
        // act
        calculator.add("-1,-2,3");
    }

当未引发异常时,您将收到以下消息: java.lang.AssertionError:预期的异常:java.lang.IllegalArgumentException

使用这种方法时,您需要小心。 有时很容易想到一般的ExceptionRuntimeException甚至Throwable 。 这被认为是一种不好的做法,因为您的代码可能会在实际未预期的地方引发异常,并且测试仍将通过!

综上所述,在我的代码中,我使用两种方法: JUnit规则注释 。 优点是:

  • 代码不引发异常时的错误消息会自动处理
  • 可读性得到改善
  • 创建的代码更少

您的喜好是什么?

我听说过处理异常的第四种方式(我的一位同事在阅读本文后提出了建议)–使用自定义注释。

乍一看,实际上该解决方案看起来不错,但是它需要您自己的JUnit运行程序,因此它有缺点:您不能将此批注与Mockito运行程序一起使用。

作为编码实践,我创建了这样的注释,所以也许有人觉得它有用

用法
@RunWith(ExpectsExceptionRunner.class)
public class StringCalculatorTest {
    @Test
    @ExpectsException(type = IllegalArgumentException.class, message = "negatives not allowed: [-1]")
    public void throwsExceptionWhenNegativeNumbersAreGiven() throws Exception {
        // act
        calculator.add("-1,-2,3");
    }

}

上面的测试将失败,并显示一条消息: java.lang.Exception:意外的异常消息,预期的

但是是

注释
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ExpectsException {
    Class type();

    String message() default "";
}

带有复制和粘贴代码的跑步者

public class ExpectsExceptionRunner extends BlockJUnit4ClassRunner {
    public ExpectsExceptionRunner(Class klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected Statement possiblyExpectingExceptions(FrameworkMethod method, Object test, Statement next) {
        ExpectsException annotation = method.getAnnotation(ExpectsException.class);
        if (annotation == null) {
            return next;
        }
        return new ExpectExceptionWithMessage(next, annotation.type(), annotation.message());
    }

    class ExpectExceptionWithMessage extends Statement {

        private final Statement next;
        private final Class expected;
        private final String expectedMessage;

        public ExpectExceptionWithMessage(Statement next, Class expected, String expectedMessage) {
            this.next = next;
            this.expected = expected;
            this.expectedMessage = expectedMessage;
        }

        @Override
        public void evaluate() throws Exception {
            boolean complete = false;
            try {
                next.evaluate();
                complete = true;
            } catch (AssumptionViolatedException e) {
                throw e;
            } catch (Throwable e) {
                if (!expected.isAssignableFrom(e.getClass())) {
                    String message = "Unexpected exception, expected<"
                            + expected.getName() + "> but was <"
                            + e.getClass().getName() + ">";
                    throw new Exception(message, e);
                }

                if (isNotNull(expectedMessage) && !expectedMessage.equals(e.getMessage())) {
                    String message = "Unexpected exception message, expected<"
                            + expectedMessage + "> but was<"
                            + e.getMessage() + ">";
                    throw new Exception(message, e);
                }
            }
            if (complete) {
                throw new AssertionError("Expected exception: "
                        + expected.getName());
            }
        }

        private boolean isNotNull(String s) {
            return s != null && !s.isEmpty();
        }
    }

}

参考: 3种处理JUnit中异常的方法。 选择哪一个? 从我们的JCG合作伙伴 Rafal Borowiec在Codeleak.pl博客上获得。

翻译自: https://www.javacodegeeks.com/2013/11/3-ways-of-handling-exceptions-in-junit-which-one-to-choose.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值