JUnit:使用Java 8和Lambda表达式测试异常

在JUnit中,有许多方法可以在测试代码中测试异常,包括try-catch idiom JUnit @Rulecatch-exception库。 从Java 8开始,我们还有另一种处理异常的方法:使用lambda表达式。 在这篇简短的博客文章中,我将演示一个简单的示例,说明如何利用Java 8和lambda表达式的功能来测试JUnit中的异常。

注意:撰写此博客文章的动机是在catch-exception项目页面上发布的消息:


Java 8的lambda表达式将使catch-exception冗余。 因此,该项目将不再维护

SUT –被测系统

我们将测试以下2类抛出的异常。

第一个:

class DummyService {
    public void someMethod() {
        throw new RuntimeException("Runtime exception occurred");
    }

    public void someOtherMethod() {
        throw new RuntimeException("Runtime exception occurred",
                new IllegalStateException("Illegal state"));
    }
}

第二个:

class DummyService2 {
    public DummyService2() throws Exception {
        throw new Exception("Constructor exception occurred");
    }

    public DummyService2(boolean dummyParam) throws Exception {
        throw new Exception("Constructor exception occurred");
    }
}

所需语法

我的目标是实现与catch-exception库接近的语法:

package com.github.kolorobot.exceptions.java8;

import org.junit.Test;
import static com.github.kolorobot.exceptions.java8.ThrowableAssertion.assertThrown;

public class Java8ExceptionsTest {

    @Test
    public void verifiesTypeAndMessage() {
        assertThrown(new DummyService()::someMethod) // method reference
                // assertions
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Runtime exception occurred")
                .hasNoCause();
    }

    @Test
    public void verifiesCauseType() {
        assertThrown(() -> new DummyService().someOtherMethod(true)) // lambda expression
                // assertions
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Runtime exception occurred")
                .hasCauseInstanceOf(IllegalStateException.class);
    }

    @Test
    public void verifiesCheckedExceptionThrownByDefaultConstructor() {
        assertThrown(DummyService2::new) // constructor reference
                // assertions
                .isInstanceOf(Exception.class)
                .hasMessage("Constructor exception occurred");
    }

    @Test
    public void verifiesCheckedExceptionThrownConstructor() {
        assertThrown(() -> new DummyService2(true)) // lambda expression
                // assertions
                .isInstanceOf(Exception.class)
                .hasMessage("Constructor exception occurred");
    }

    @Test(expected = ExceptionNotThrownAssertionError.class) // making test pass
    public void failsWhenNoExceptionIsThrown() {
        // expected exception not thrown
        assertThrown(() -> System.out.println());
    }
}

注意:与catch-exception相比的优势在于,我们将能够测试引发异常的构造函数。

创建“图书馆”

合成糖

assertThrown是一个静态工厂方法,它使用对捕获的异常的引用来创建ThrowableAssertion的新实例。

package com.github.kolorobot.exceptions.java8;

public class ThrowableAssertion {
    public static ThrowableAssertion assertThrown(ExceptionThrower exceptionThrower) {
        try {
            exceptionThrower.throwException();
        } catch (Throwable caught) {
            return new ThrowableAssertion(caught);
        }
        throw new ExceptionNotThrownAssertionError();
    }

    // other methods omitted for now
}

ExceptionThrower是一个@FunctionalInterface ,可以使用lambda表达式,方法引用或构造函数引用创建实例。 assertThrown接受ExceptionThrower将期望并准备处理异常。

@FunctionalInterface
public interface ExceptionThrower {
    void throwException() throws Throwable;
}

断言

最后,我们需要创建一些断言,以便可以在测试代码中验证关于teste异常的解释。 实际上, ThrowableAssertion是一种自定义断言,为我们提供了一种有效地验证所捕获异常的方法。 在下面的代码中,我使用了Hamcrest匹配器来创建断言。 ThrowableAssertion类的完整来源:

package com.github.kolorobot.exceptions.java8;

import org.hamcrest.Matchers;
import org.junit.Assert;

public class ThrowableAssertion {

    public static ThrowableAssertion assertThrown(ExceptionThrower exceptionThrower) {
        try {
            exceptionThrower.throwException();
        } catch (Throwable caught) {
            return new ThrowableAssertion(caught);
        }
        throw new ExceptionNotThrownAssertionError();
    }

    private final Throwable caught;

    public ThrowableAssertion(Throwable caught) {
        this.caught = caught;
    }

    public ThrowableAssertion isInstanceOf(Class<? extends Throwable> exceptionClass) {
        Assert.assertThat(caught, Matchers.isA((Class<Throwable>) exceptionClass));
        return this;
    }

    public ThrowableAssertion hasMessage(String expectedMessage) {
        Assert.assertThat(caught.getMessage(), Matchers.equalTo(expectedMessage));
        return this;
    }

    public ThrowableAssertion hasNoCause() {
        Assert.assertThat(caught.getCause(), Matchers.nullValue());
        return this;
    }

    public ThrowableAssertion hasCauseInstanceOf(Class<? extends Throwable> exceptionClass) {
        Assert.assertThat(caught.getCause(), Matchers.notNullValue());
        Assert.assertThat(caught.getCause(), Matchers.isA((Class<Throwable>) exceptionClass));
        return this;
    }
}

AssertJ实施

如果您使用AssertJ库,则可以使用AssertJ轻松创建ThrowableAssertion AssertJ版本,它提供了许多org.assertj.core.api.ThrowableAssert断言。 该类的实现比上面介绍的Hamcrest更简单。

package com.github.kolorobot.exceptions.java8;

import org.assertj.core.api.Assertions;
import org.assertj.core.api.ThrowableAssert;

public class AssertJThrowableAssert {
    public static ThrowableAssert assertThrown(ExceptionThrower exceptionThrower) {
        try {
            exceptionThrower.throwException();
        } catch (Throwable throwable) {
            return Assertions.assertThat(throwable);
        }
        throw new ExceptionNotThrownAssertionError();
    }
}

AssertJ的示例测试:

public class AssertJJava8ExceptionsTest {
    @Test
    public void verifiesTypeAndMessage() {
        assertThrown(new DummyService()::someMethod)
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Runtime exception occurred")
                .hasMessageStartingWith("Runtime")
                .hasMessageEndingWith("occurred")
                .hasMessageContaining("exception")
                .hasNoCause();
    }
}

摘要

仅用几行代码,我们构建了非常酷的代码,可帮助我们在JUnit中测试异常,而无需任何其他库。 这仅仅是一个开始。 利用Java 8和lambda表达式的强大功能!

资源资源

  • GitHub上提供了本文的源代码 (请看com.github.kolorobot.exceptions.java8包)
  • 我的其他一些文章关于在JUnit中测试异常。 请看一看:

翻译自: https://www.javacodegeeks.com/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值