是否可以使用 JUnit Rule ExpectedException 访问异常 "catched" 的实例?

Is it possible to access instance of exception "catched" with JUnit Rule ExpectedException?

假设在一个测试中是:

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

一些测试包含:

thrown.expect(SomeNastyException.class);

现在如果有需要检查这个SomeNastyException更详细的方法是什么?

当然可以不使用 ExcpectedException 并使用 try/catch 包装调用,然后以任何需要的方式检查异常但现在的问题是可以用 ExpectedException 来完成,比如:

assertEquals("42", thrown.getException().getUniversalAnswerToEverything());

该规则中有一个不太专业的 expect 方法版本,它接受 Hamcrest 的匹配器:

expect(Matcher)

只要有一个 Matcher 实现,此方法将允许您对抛出的异常断言几乎所有内容。

例如,对于您的情况,它看起来像这样:

import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;

....

exceptions.expect(hasProperty("UniversalAnswerToEverything", is(42)));

Hamcrest 有一个非常灵活的匹配器模型,因此如果您对库中包含的内容不满意,也可以轻松编写自己的匹配器模型。

您可以使用 hasProperty hamcrest Matcher。如果您的 SomeNastyException 不符合 java bean 协议,您可以为您的异常创建一个自定义的 hamcrest 匹配器:

package Whosebug.q59946794;

import org.hamcrest.Description;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.hamcrest.core.IsEqual;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.util.logging.Logger;

public class SomeNastyExceptionSpec {
    private final static Logger logger = Logger.getLogger(SomeNastyExceptionSpec.class.getName());

    public static class SomeNastyException extends Exception implements java.io.Serializable {
        SomeNastyException(String message) { super(message);}
        public Integer getUniversalAnswerToEverything() {return 42;}
    }

    public static Matcher<SomeNastyException> hasUniversalAnswerToEverythingFeature(final int expectedAnswer) {
        return new FeatureMatcher<SomeNastyException, Integer>(
                new IsEqual<Integer>(expectedAnswer),
                "SomeNastyException actual answer",
                "SomeNastyException expected answer"
        ) {
            @Override
            protected Integer featureValueOf(SomeNastyException actual) {
                return actual.getUniversalAnswerToEverything();
            }
        };

    }
    public static Matcher<SomeNastyException> hasUniversalAnswerToEverything(final int expectedAnswer) {
        return new TypeSafeMatcher<SomeNastyException>() {

            @Override
            protected void describeMismatchSafely(SomeNastyException e, Description description) {
                description.appendText("was ").appendValue(e.getUniversalAnswerToEverything());
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("SomeNastyException with answer ").appendValue(expectedAnswer);
            }

            @Override
            protected boolean matchesSafely(SomeNastyException e) {
                return e.getUniversalAnswerToEverything() == expectedAnswer;
            }
        };
    }

    @Rule(order = Integer.MAX_VALUE)
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void shouldTestExceptionMessage() throws Exception {
        thrown.expect(SomeNastyException.class);
        thrown.expect(hasUniversalAnswerToEverything(42));
        thrown.expect(hasUniversalAnswerToEverythingFeature(42));
        thrown.expectMessage("always 42");
        throw new SomeNastyException("always 42");
    }
}

我的问题是在仍在使用 JUnit4 时提出的。最近迁移到 JUnit5,它正是我要找的东西,即 assertThrows returns 抛出的异常。作为一个虚拟的例子:

@org.junit.jupiter.api.Test
void testThrows() {
    Exception exception = assertThrows(NotAnswerableException.class, () -> {
        throw new NotAnswerableException("please rephrase your question", param2, param3);
    });
    assertTrue(exception.getMessage().startsWith("please"));
    assertEquals(param2, exception.getParam2());
}