mockito: ClassCastExceptions with JDK9 javac
JDK 9 fixes a javac bug (JDK-8058199) that was causing checkcast intructions to be skipped. Previously javac used the parameter types of a method symbol’s erased type as targets when translating the arguments. In JDK 9, javac has been fixed to use the inferred types as targets. The fix causes additional checkcasts to be generated if the inferred types do not have the same erasure.
The fix breaks Mockito answer strategies that pick types based on the erased method signature’s return type, and causes tests to fail with ClassCastExceptions when compiled with the JDK 9 javac.
Example 1
class Super<T> {
T g() {
return null;
}
}
class Sub extends Super<Boolean> {}
@Mock Sub s;
when(s.g()).thenReturn(false);
compiled with javac 8
INVOKEVIRTUAL Sub.g ()Ljava/lang/Object;
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;
compiled with javac 9
INVOKEVIRTUAL Sub.g ()Ljava/lang/Object;
CHECKCAST java/lang/Boolean
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;
The erased return type of Super.g
is Object
, but the expected return type of Sub.g
is Boolean
. If the answer strategy returns Object
the checkcast fails.
Example 2
class Foo {
<T> T getFirst(Iterable<T> xs) { return xs.iterator().next(); }
}
@Mock Foo f;
Iterable<Boolean> it = Arrays.asList(false);
when(f.getFirst(it)).thenReturn(false)
compiled with javac 8
INVOKEVIRTUAL Foo.getFirst (Ljava/lang/Iterable;)Ljava/lang/Object;
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;
compiled with javac 9
INVOKEVIRTUAL Foo.getFirst (Ljava/lang/Iterable;)Ljava/lang/Object;
CHECKCAST java/lang/Boolean
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;
The erased return type of Foo.getFirst
is Object
, but the inferred return type of getFirst(Iterable<Boolean>)
is Boolean
. If the answer strategy returns Object
the checkcast fails.
The first example could be fixed by using GenericMetadataSupport
in all of the answer implementations instead of invocation.getMethod().getReturnType()
.
It gets more difficult if the mock’s type is an instantiation of a generic type (e.g. @Mock Foo<Bar> x;
), since the field’s type arguments get dropped. I think fixing that would require adding support for mocking types, not just classes.
For the second example, returning the right answer requires considering the generic signature of the invoked method, and performing type inference using the argument types. Unfortunately the runtime type of the argument is going to be a raw Iterable
and the inference depends on knowing it’s Iterable<Boolean>
, so I’m not sure what to do there.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 2
- Comments: 23 (22 by maintainers)
Commits related to this issue
- Add tests to demonstrate #357 — committed to cushon/mockito by cushon 8 years ago
- Add tests to demonstrate #357 — committed to mockito/mockito by cushon 8 years ago
- Fix scan tests that use mocks of non-Scannable PARENTs/ACTUALs Added MockUtils with interfaces that help creating Scannable mocks that also implement another interface for combinations not naturally ... — committed to reactor/reactor-core by simonbasle 7 years ago
- Fix scan tests that use mocks of non-Scannable PARENTs/ACTUALs Added MockUtils with interfaces that help creating Scannable mocks that also implement another interface for combinations not naturally ... — committed to reactor/reactor-core by simonbasle 7 years ago
- [build] #881 Mostly build under JDK9, add JDK9 build in Travis CI This commit builds under both JDK8 and JDK9 in Travis CI. Minus a couple of caveats, it fixes the JDK9 build with various changes: V... — committed to reactor/reactor-core by simonbasle 7 years ago
- [build] Fix #891 Mostly build under JDK9, add JDK9 build in Travis CI This commit builds under both JDK8 and JDK9 in Travis CI. Minus a couple of caveats, it fixes the JDK9 build with various changes... — committed to reactor/reactor-core by simonbasle 7 years ago
- [build] Fix #891 Mostly build under JDK9, add JDK9 build in Travis CI This commit builds under both JDK8 and JDK9 in Travis CI. Minus a couple of caveats, it fixes the JDK9 build with various changes... — committed to reactor/reactor-core by simonbasle 7 years ago
- Remove Corba and https://github.com/mockito/mockito/issues/357 — committed to MishaDemianenko/neo4j-public by MishaDemianenko 7 years ago
- Work around https://github.com/mockito/mockito/issues/357 — committed to Pr0methean/Glowstone by Pr0methean 6 years ago
- DATAJDBC-293 - Remove usage of RETURNS_DEEP_STUBS. This is broken from JDK9 on for generic types. See also: https://github.com/mockito/mockito/issues/357 — committed to spring-projects/spring-data-relational by schauder 5 years ago
- Return null instead of causing a CCE This solves a large number of edge-cases where `null` will actually remove the runtime ClassCastException. This essentially negates the whole MockitoCast ErrorPro... — committed to mockito/mockito by TimvdLippe 5 years ago
- Return null instead of causing a CCE (#1612) This solves a large number of edge-cases where `null` will actually remove the runtime ClassCastException. This essentially negates the whole MockitoCas... — committed to mockito/mockito by TimvdLippe 5 years ago
- Return null instead of causing a CCE (#1612) This solves a large number of edge-cases where `null` will actually remove the runtime ClassCastException. This essentially negates the whole MockitoCas... — committed to SamBarker/mockito by TimvdLippe 5 years ago
- Reintroduces the guard that returns null instead of a mock of Object While the generics metadata extractor has been improved, not every possible scenarios are covered even Javac in version 8 do not f... — committed to bric3/mockito by bric3 5 years ago
- PLC4X-86 - Fix and re-enable tests that were disabled for Java 11 support Some of the previously uncommented tests now pass with Java 8 and Java 11. The fixes mostly consist of workarounds around Moc... — committed to thomasdarimont/plc4x by thomasdarimont 5 years ago
- PLC4X-86 - Fix and re-enable tests that were disabled for Java 11 support Some of the previously uncommented tests now pass with Java 8 and Java 11. The fixes mostly consist of workarounds around Moc... — committed to vemmert/plc4x by thomasdarimont 5 years ago
- Turn down MockitoCastTest This was a temporary check to support upgrading to the JDK 9 javac. The upstream mockito bug is fixed: https://github.com/mockito/mockito/issues/357 Closes https://github.... — committed to google/error-prone by cushon 4 years ago
- Turn down MockitoCastTest This was a temporary check to support upgrading to the JDK 9 javac. The upstream mockito bug is fixed: https://github.com/mockito/mockito/issues/357 Closes https://github.... — committed to google/error-prone by cushon 4 years ago
Only when returning automatic mocks from mocks with generic return types, maybe we should add a warning for this to make the error more intuitive.