Skip to content

Commit bdb1799

Browse files
rschmittmarcphilipp
authored andcommitted
Add Assert#expectThrows and Assert#assertThrows
Closes #1154.
1 parent bcbf43d commit bdb1799

File tree

2 files changed

+143
-4
lines changed

2 files changed

+143
-4
lines changed

src/main/java/org/junit/Assert.java

+63-3
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public static void assertArrayEquals(String message, Object[] expecteds,
298298
public static void assertArrayEquals(Object[] expecteds, Object[] actuals) {
299299
assertArrayEquals(null, expecteds, actuals);
300300
}
301-
301+
302302
/**
303303
* Asserts that two boolean arrays are equal. If they are not, an
304304
* {@link AssertionError} is thrown with the given message. If
@@ -313,8 +313,8 @@ public static void assertArrayEquals(Object[] expecteds, Object[] actuals) {
313313
public static void assertArrayEquals(String message, boolean[] expecteds,
314314
boolean[] actuals) throws ArrayComparisonFailure {
315315
internalArrayEquals(message, expecteds, actuals);
316-
}
317-
316+
}
317+
318318
/**
319319
* Asserts that two boolean arrays are equal. If they are not, an
320320
* {@link AssertionError} is thrown. If <code>expected</code> and
@@ -955,4 +955,64 @@ public static <T> void assertThat(String reason, T actual,
955955
Matcher<? super T> matcher) {
956956
MatcherAssert.assertThat(reason, actual, matcher);
957957
}
958+
959+
/**
960+
* This interface facilitates the use of expectThrows from Java 8. It allows method references
961+
* to void methods (that declare checked exceptions) to be passed directly into expectThrows
962+
* without wrapping. It is not meant to be implemented directly.
963+
*
964+
* @since 4.13
965+
*/
966+
public interface ThrowingRunnable {
967+
void run() throws Throwable;
968+
}
969+
970+
/**
971+
* Asserts that {@code runnable} throws an exception of type {@code expectedThrowable} when
972+
* executed. If it does not throw an exception, an {@link AssertionError} is thrown. If it
973+
* throws the wrong type of exception, an {@code AssertionError} is thrown describing the
974+
* mismatch; the exception that was actually thrown can be obtained by calling {@link
975+
* AssertionError#getCause}.
976+
*
977+
* @param expectedThrowable the expected type of the exception
978+
* @param runnable a function that is expected to throw an exception when executed
979+
* @since 4.13
980+
*/
981+
public static void assertThrows(Class<? extends Throwable> expectedThrowable, ThrowingRunnable runnable) {
982+
expectThrows(expectedThrowable, runnable);
983+
}
984+
985+
/**
986+
* Asserts that {@code runnable} throws an exception of type {@code expectedThrowable} when
987+
* executed. If it does, the exception object is returned. If it does not throw an exception, an
988+
* {@link AssertionError} is thrown. If it throws the wrong type of exception, an {@code
989+
* AssertionError} is thrown describing the mismatch; the exception that was actually thrown can
990+
* be obtained by calling {@link AssertionError#getCause}.
991+
*
992+
* @param expectedThrowable the expected type of the exception
993+
* @param runnable a function that is expected to throw an exception when executed
994+
* @return the exception thrown by {@code runnable}
995+
* @since 4.13
996+
*/
997+
public static <T extends Throwable> T expectThrows(Class<T> expectedThrowable, ThrowingRunnable runnable) {
998+
try {
999+
runnable.run();
1000+
} catch (Throwable actualThrown) {
1001+
if (expectedThrowable.isInstance(actualThrown)) {
1002+
@SuppressWarnings("unchecked") T retVal = (T) actualThrown;
1003+
return retVal;
1004+
} else {
1005+
String mismatchMessage = format("unexpected exception type thrown;",
1006+
expectedThrowable.getSimpleName(), actualThrown.getClass().getSimpleName());
1007+
1008+
// The AssertionError(String, Throwable) ctor is only available on JDK7.
1009+
AssertionError assertionError = new AssertionError(mismatchMessage);
1010+
assertionError.initCause(actualThrown);
1011+
throw assertionError;
1012+
}
1013+
}
1014+
String message = String.format("expected %s to be thrown, but nothing was thrown",
1015+
expectedThrowable.getSimpleName());
1016+
throw new AssertionError(message);
1017+
}
9581018
}

src/test/java/org/junit/tests/assertion/AssertionTest.java

+80-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
import static org.junit.Assert.assertSame;
1212
import static org.junit.Assert.assertThat;
1313
import static org.junit.Assert.assertTrue;
14+
import static org.junit.Assert.expectThrows;
1415
import static org.junit.Assert.fail;
1516

17+
import java.io.IOException;
1618
import java.math.BigDecimal;
1719

1820
import org.junit.Assert;
21+
import org.junit.Assert.ThrowingRunnable;
1922
import org.junit.ComparisonFailure;
2023
import org.junit.Test;
2124
import org.junit.internal.ArrayComparisonFailure;
@@ -170,7 +173,7 @@ public void oneDimensionalDoubleArraysAreNotEqual() {
170173
public void oneDimensionalFloatArraysAreNotEqual() {
171174
assertArrayEquals(new float[]{1.0f}, new float[]{2.5f}, 1.0f);
172175
}
173-
176+
174177
@Test(expected = AssertionError.class)
175178
public void oneDimensionalBooleanArraysAreNotEqual() {
176179
assertArrayEquals(new boolean[]{true}, new boolean[]{false});
@@ -674,4 +677,80 @@ public void assertNotEqualsIgnoresDeltaOnNaN() {
674677
public void assertNotEqualsIgnoresFloatDeltaOnNaN() {
675678
assertNotEquals(Float.NaN, Float.NaN, 1f);
676679
}
680+
681+
@Test(expected = AssertionError.class)
682+
public void expectThrowsRequiresAnExceptionToBeThrown() {
683+
expectThrows(Throwable.class, nonThrowingRunnable());
684+
}
685+
686+
@Test
687+
public void expectThrowsIncludesAnInformativeDefaultMessage() {
688+
try {
689+
expectThrows(Throwable.class, nonThrowingRunnable());
690+
} catch (AssertionError ex) {
691+
assertEquals("expected Throwable to be thrown, but nothing was thrown", ex.getMessage());
692+
return;
693+
}
694+
fail();
695+
}
696+
697+
@Test
698+
public void expectThrowsReturnsTheSameObjectThrown() {
699+
NullPointerException npe = new NullPointerException();
700+
701+
Throwable throwable = expectThrows(Throwable.class, throwingRunnable(npe));
702+
703+
assertSame(npe, throwable);
704+
}
705+
706+
@Test(expected = AssertionError.class)
707+
public void expectThrowsDetectsTypeMismatchesViaExplicitTypeHint() {
708+
NullPointerException npe = new NullPointerException();
709+
710+
expectThrows(IOException.class, throwingRunnable(npe));
711+
}
712+
713+
@Test
714+
public void expectThrowsWrapsAndPropagatesUnexpectedExceptions() {
715+
NullPointerException npe = new NullPointerException("inner-message");
716+
717+
try {
718+
expectThrows(IOException.class, throwingRunnable(npe));
719+
} catch (AssertionError ex) {
720+
assertSame(npe, ex.getCause());
721+
assertEquals("inner-message", ex.getCause().getMessage());
722+
return;
723+
}
724+
fail();
725+
}
726+
727+
@Test
728+
public void expectThrowsSuppliesACoherentErrorMessageUponTypeMismatch() {
729+
NullPointerException npe = new NullPointerException();
730+
731+
try {
732+
expectThrows(IOException.class, throwingRunnable(npe));
733+
} catch (AssertionError error) {
734+
assertEquals("unexpected exception type thrown; expected:<IOException> but was:<NullPointerException>",
735+
error.getMessage());
736+
assertSame(npe, error.getCause());
737+
return;
738+
}
739+
fail();
740+
}
741+
742+
private static ThrowingRunnable nonThrowingRunnable() {
743+
return new ThrowingRunnable() {
744+
public void run() throws Throwable {
745+
}
746+
};
747+
}
748+
749+
private static ThrowingRunnable throwingRunnable(final Throwable t) {
750+
return new ThrowingRunnable() {
751+
public void run() throws Throwable {
752+
throw t;
753+
}
754+
};
755+
}
677756
}

0 commit comments

Comments
 (0)