// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.testutils;
import static org.openstreetmap.josm.testutils.ThrowableRootCauseMatcher.hasRootCause;
import org.hamcrest.Matcher;
import org.junit.rules.ExpectedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* The {@code ExpectedRootException} behaves exactly as JUnit's {@link ExpectedException} rule.
* This class is needed to add {@link #expectRootCause} method, which has been rejected by JUnit developers,
* and {@code ExpectedException} cannot be extended because it has a private constructor.
* @see <a href="https://github.com/junit-team/junit4/pull/778">Github pull request</a>
*/
public final class ExpectedRootException implements TestRule {
private final ExpectedException rule = ExpectedException.none();
/**
* Returns a {@linkplain TestRule rule} that expects no exception to be thrown (identical to behavior without this rule).
* @return {@code ExpectedRootException} instance
*/
@SuppressFBWarnings("NM_CLASS_NOT_EXCEPTION")
public static ExpectedRootException none() {
return new ExpectedRootException();
}
private ExpectedRootException() {
}
/**
* Specifies the failure message for tests that are expected to throw an exception but do not throw any.
* You can use a {@code %s} placeholder for the description of the expected exception.
* E.g. "Test doesn't throw %s." will fail with the error message "Test doesn't throw an instance of foo.".
*
* @param message exception detail message
* @return the rule itself
*/
public ExpectedRootException reportMissingExceptionWithMessage(String message) {
rule.reportMissingExceptionWithMessage(message);
return this;
}
@Override
public Statement apply(Statement base, Description description) {
return rule.apply(base, description);
}
/**
* Verify that your code throws an exception that is an instance of specific {@code type}.
* <pre> @Test
* public void throwsExceptionWithSpecificType() {
* thrown.expect(NullPointerException.class);
* throw new NullPointerException();
* }</pre>
* @param type Throwable type
* @return {@code this}
*/
public ExpectedRootException expect(Class<? extends Throwable> type) {
rule.expect(type);
return this;
}
/**
* Verify that your code throws an exception whose message contains a specific text.
* <pre> @Test
* public void throwsExceptionWhoseMessageContainsSpecificText() {
* thrown.expectMessage("happened");
* throw new NullPointerException("What happened?");
* }</pre>
* @param substring substring to expect in error message
* @return {@code this}
*/
public ExpectedRootException expectMessage(String substring) {
rule.expectMessage(substring);
return this;
}
/**
* Verify that your code throws an exception whose immediate cause is matched by the given Hamcrest matcher.
* <pre> @Test
* public void throwsExceptionWhoseCauseCompliesWithMatcher() {
* NullPointerException rootCause = new NullPointerException();
* IllegalStateException immediateCause = new IllegalStateException(rootCause);
* thrown.expectCause(isA(NullPointerException.class));
* throw new IllegalArgumentException(immediateCause);
* }</pre>
* @param expectedCause expected cause
* @return {@code this}
*/
public ExpectedRootException expectCause(Matcher<? extends Throwable> expectedCause) {
rule.expectCause(expectedCause);
return this;
}
/**
* Verify that you code throws an exception whose root cause is matched by the given Hamcrest matcher.
* <pre> @Test
* public void throwsExceptionWhoseRootCauseCompliesWithMatcher() {
* NullPointerException rootCause = new NullPointerException();
* IllegalStateException immediateCause = new IllegalStateException(rootCause);
* thrown.expectRootCause(isA(NullPointerException.class));
* throw new IllegalArgumentException(immediateCause);
* }</pre>
* @param expectedRootCause expected root cause
* @return {@code this}
*/
public ExpectedRootException expectRootCause(Matcher<? extends Throwable> expectedRootCause) {
rule.expect(hasRootCause(expectedRootCause));
return this;
}
}