Skip to content

Commit 66ef6ef

Browse files
committed
Remove variants of assertThrows accepting ThrowingSupplier
JUnit Jupiter 5.3.0 introduced new variants of `assertThrows()` that accept `ThrowingSupplier` arguments instead of `Executable` (see #1394). However, this change prevents existing code from compiling against Jupiter 5.3.0 if the code in question used a method reference for an overloaded method with a `void` return type. Consequently, this was a breaking change. Note that overloaded methods with non-void return types are not affected. For example, even though `java.util.concurrent.Future` has two `get(...)` methods, it still can be used as a method reference as `future::get` without suffering from issues with type inference since such a method can always properly be inferred to be a `ThrowingSupplier`. This commit fixes the breaking change by removing all variants of `assertThrows` that accept a `ThrowingSupplier`. In addition, this commit reverts the related changes to `ThrowingSupplier` (i.e., it no longer implements `Executable` via a `default` interface method). Issue: #1576
1 parent edc48c3 commit 66ef6ef

File tree

6 files changed

+31
-257
lines changed

6 files changed

+31
-257
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.3.1.adoc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[[release-notes-5.3.1]]
22
== 5.3.1
33

4-
*Date of Release:*
4+
*Date of Release:* September ❓, 2018
55

66
*Scope:* Bug fixes since 5.3.0
77

@@ -30,7 +30,16 @@ repository on GitHub.
3030

3131
==== Bug Fixes
3232

33-
* ❓
33+
* Invocations of `assertThrows()` that are passed a method reference for an overloaded
34+
method with a `void` return type once again compile.
35+
- For example, given an instance of `java.lang.Object` stored in a variable named
36+
`object`, `assertThrows(Exception.class, object::wait)` compiled against JUnit 5.2.0,
37+
failed to compile against JUnit 5.3.0, but now compiles against JUnit 5.3.1.
38+
39+
==== Breaking Changes
40+
41+
* In order to revert the aforementioned breaking change, variants of `assertThrows()`
42+
introduced in JUnit 5.3.0 that accept `ThrowingSupplier` arguments have been removed.
3443

3544

3645
[[release-notes-5.3.1-junit-vintage]]

junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java

Lines changed: 6 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import java.util.function.Supplier;
1919

2020
import org.junit.jupiter.api.function.Executable;
21-
import org.junit.jupiter.api.function.ThrowingSupplier;
22-
import org.junit.platform.commons.util.StringUtils;
2321
import org.opentest4j.AssertionFailedError;
2422

2523
/**
@@ -35,49 +33,25 @@ private AssertThrows() {
3533
}
3634

3735
static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) {
38-
return assertThrows(expectedType, asSupplier(executable), (Object) null);
36+
return assertThrows(expectedType, executable, (Object) null);
3937
}
4038

4139
static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable, String message) {
42-
return assertThrows(expectedType, asSupplier(executable), (Object) message);
40+
return assertThrows(expectedType, executable, (Object) message);
4341
}
4442

4543
static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,
4644
Supplier<String> messageSupplier) {
4745

48-
return assertThrows(expectedType, asSupplier(executable), (Object) messageSupplier);
49-
}
50-
51-
/**
52-
* @since 5.3
53-
*/
54-
static <T extends Throwable> T assertThrows(Class<T> expectedType, ThrowingSupplier<?> supplier) {
55-
return assertThrows(expectedType, supplier::get, (Object) null);
56-
}
57-
58-
/**
59-
* @since 5.3
60-
*/
61-
static <T extends Throwable> T assertThrows(Class<T> expectedType, ThrowingSupplier<?> supplier, String message) {
62-
return assertThrows(expectedType, supplier::get, (Object) message);
63-
}
64-
65-
/**
66-
* @since 5.3
67-
*/
68-
static <T extends Throwable> T assertThrows(Class<T> expectedType, ThrowingSupplier<?> supplier,
69-
Supplier<String> messageSupplier) {
70-
71-
return assertThrows(expectedType, supplier::get, (Object) messageSupplier);
46+
return assertThrows(expectedType, executable, (Object) messageSupplier);
7247
}
7348

7449
@SuppressWarnings("unchecked")
75-
private static <T extends Throwable> T assertThrows(Class<T> expectedType, ResultAwareThrowingSupplier<?> supplier,
50+
private static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,
7651
Object messageOrSupplier) {
7752

78-
Object result;
7953
try {
80-
result = supplier.get();
54+
executable.execute();
8155
}
8256
catch (Throwable actualException) {
8357
if (expectedType.isInstance(actualException)) {
@@ -90,54 +64,9 @@ private static <T extends Throwable> T assertThrows(Class<T> expectedType, Resul
9064
}
9165
}
9266

93-
String includedResult = supplier.includeResult()
94-
? String.format(" (returned %s).", StringUtils.nullSafeToString(result))
95-
: ".";
9667
String message = buildPrefix(nullSafeGet(messageOrSupplier))
97-
+ String.format("Expected %s to be thrown, but nothing was thrown", getCanonicalName(expectedType))
98-
+ includedResult;
68+
+ String.format("Expected %s to be thrown, but nothing was thrown.", getCanonicalName(expectedType));
9969
throw new AssertionFailedError(message);
10070
}
10171

102-
private interface ResultAwareThrowingSupplier<T> extends ThrowingSupplier<T> {
103-
104-
/**
105-
* Determine if the result of invoking {@link #get()} should be included
106-
* in the assertion failure message if this supplier returns an actual
107-
* result instead of throwing an exception.
108-
*
109-
* @return {@code true} by default; can be overridden in concrete implementations
110-
*/
111-
default boolean includeResult() {
112-
return true;
113-
}
114-
}
115-
116-
private static ResultAwareThrowingSupplier<Void> asSupplier(Executable executable) {
117-
return new ResultAwareThrowingSupplierAdapter(executable);
118-
}
119-
120-
/**
121-
* Adapts an {@link Executable} to the {@link ResultAwareThrowingSupplier} API.
122-
*/
123-
private static class ResultAwareThrowingSupplierAdapter implements ResultAwareThrowingSupplier<Void> {
124-
125-
private final Executable executable;
126-
127-
ResultAwareThrowingSupplierAdapter(Executable executable) {
128-
this.executable = executable;
129-
}
130-
131-
@Override
132-
public Void get() throws Throwable {
133-
executable.execute();
134-
return null;
135-
}
136-
137-
@Override
138-
public boolean includeResult() {
139-
return false;
140-
}
141-
}
142-
14372
}

junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,75 +1211,6 @@ public static <T extends Throwable> T assertThrows(Class<T> expectedType, Execut
12111211
return AssertThrows.assertThrows(expectedType, executable, messageSupplier);
12121212
}
12131213

1214-
// --- supplier ---
1215-
1216-
/**
1217-
* <em>Asserts</em> that execution of the given {@code supplier} throws
1218-
* an exception of the {@code expectedType} and returns the exception.
1219-
*
1220-
* <p>If no exception is thrown, or if an exception of a different type is
1221-
* thrown, this method will fail.
1222-
*
1223-
* <p>If the given {@link ThrowingSupplier} returns a result instead of
1224-
* throwing an exception, the result will be included in the failure message.
1225-
*
1226-
* <p>If you do not want to perform additional checks on the exception instance,
1227-
* simply ignore the return value.
1228-
*
1229-
* @since 5.3
1230-
*/
1231-
@API(status = STABLE, since = "5.3")
1232-
public static <T extends Throwable> T assertThrows(Class<T> expectedType, ThrowingSupplier<?> supplier) {
1233-
return AssertThrows.assertThrows(expectedType, supplier);
1234-
}
1235-
1236-
/**
1237-
* <em>Asserts</em> that execution of the given {@code supplier} throws
1238-
* an exception of the {@code expectedType} and returns the exception.
1239-
*
1240-
* <p>If no exception is thrown, or if an exception of a different type is
1241-
* thrown, this method will fail.
1242-
*
1243-
* <p>If the given {@link ThrowingSupplier} returns a result instead of
1244-
* throwing an exception, the result will be included in the failure message.
1245-
*
1246-
* <p>If you do not want to perform additional checks on the exception instance,
1247-
* simply ignore the return value.
1248-
*
1249-
* @since 5.3
1250-
*/
1251-
@API(status = STABLE, since = "5.3")
1252-
public static <T extends Throwable> T assertThrows(Class<T> expectedType, ThrowingSupplier<?> supplier,
1253-
String message) {
1254-
1255-
return AssertThrows.assertThrows(expectedType, supplier, message);
1256-
}
1257-
1258-
/**
1259-
* <em>Asserts</em> that execution of the given {@code supplier} throws
1260-
* an exception of the {@code expectedType} and returns the exception.
1261-
*
1262-
* <p>If no exception is thrown, or if an exception of a different type is
1263-
* thrown, this method will fail.
1264-
*
1265-
* <p>If necessary, the failure message will be retrieved lazily from the
1266-
* supplied {@code messageSupplier}.
1267-
*
1268-
* <p>If the given {@link ThrowingSupplier} returns a result instead of
1269-
* throwing an exception, the result will be included in the failure message.
1270-
*
1271-
* <p>If you do not want to perform additional checks on the exception instance,
1272-
* simply ignore the return value.
1273-
*
1274-
* @since 5.3
1275-
*/
1276-
@API(status = STABLE, since = "5.3")
1277-
public static <T extends Throwable> T assertThrows(Class<T> expectedType, ThrowingSupplier<?> supplier,
1278-
Supplier<String> messageSupplier) {
1279-
1280-
return AssertThrows.assertThrows(expectedType, supplier, messageSupplier);
1281-
}
1282-
12831214
// --- executable ---
12841215

12851216
/**

junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingSupplier.java

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,6 @@
2323
* {@link java.util.function.Supplier}, except that a {@code ThrowingSupplier}
2424
* can throw any kind of exception, including checked exceptions.
2525
*
26-
* <p>As of JUnit Jupiter 5.3, {@code ThrowingSupplier} extends
27-
* {@link Executable}, providing a <em>default</em> implementation of
28-
* {@link #execute()} that delegates to {@link #get()} and ignores the return
29-
* value. This allows the Java compiler to disambiguate between
30-
* {@code ThrowingSupplier} and {@code Executable} when performing type
31-
* inference for a lambda expression or method reference supplied to
32-
* an overloaded method that accepts either a {@code ThrowingSupplier} or an
33-
* {@code Executable}.
34-
*
3526
* <h4>Rationale for throwing {@code Throwable} instead of {@code Exception}</h4>
3627
*
3728
* <p>Although Java applications typically throw exceptions that are instances
@@ -51,23 +42,7 @@
5142
*/
5243
@FunctionalInterface
5344
@API(status = STABLE, since = "5.0")
54-
public interface ThrowingSupplier<T> extends Executable {
55-
56-
/**
57-
* Delegates to {@link #get()} and ignores the return value.
58-
*
59-
* <p>This default method is not intended to be overridden. See
60-
* {@linkplain ThrowingSupplier class-level documentation} for further
61-
* details.
62-
*
63-
* @since 5.3
64-
* @see #get()
65-
*/
66-
@Override
67-
@API(status = STABLE, since = "5.3")
68-
default void execute() throws Throwable {
69-
get();
70-
}
45+
public interface ThrowingSupplier<T> {
7146

7247
/**
7348
* Get a result, potentially throwing an exception.

junit-jupiter-engine/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,19 @@ class AssertDoesNotThrowAssertionsTests {
3535
private static final ThrowingSupplier<String> something = () -> "enigma";
3636

3737
@Test
38-
void assertDoesNotThrowWithFutureMethodReference() {
38+
void assertDoesNotThrowWithMethodReferenceForNonVoidReturnType() {
3939
FutureTask<String> future = new FutureTask<>(() -> {
4040
return "foo";
4141
});
4242
future.run();
4343

4444
String result;
4545

46-
// Current compiler's type inference
47-
result = assertDoesNotThrow(future::get);
48-
assertEquals("foo", result);
46+
// Current compiler's type inference: does NOT compile since the compiler
47+
// cannot figure out which overloaded variant of assertDoesNotThrow() to
48+
// invoke (i.e., Executable vs. ThrowingSupplier).
49+
//
50+
// result = assertDoesNotThrow(future::get);
4951

5052
// Explicitly as an Executable
5153
assertDoesNotThrow((Executable) future::get);
@@ -61,7 +63,9 @@ void assertDoesNotThrowWithMethodReferenceForVoidReturnType() {
6163

6264
// Note: the following does not compile since the compiler cannot properly
6365
// perform type inference for a method reference for an overloaded method
64-
// that has a void return type such as Foo.overloaded(...)
66+
// that has a void return type such as Foo.overloaded(...), IFF the
67+
// compiler is simultaneously trying to pick which overloaded variant
68+
// of assertDoesNotThrow() to invoke.
6569
//
6670
// assertDoesNotThrow(foo::overloaded);
6771

0 commit comments

Comments
 (0)