File: MockitoCast.md

package info (click to toggle)
error-prone-java 2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 15,076 kB
  • sloc: java: 171,398; xml: 1,459; sh: 34; makefile: 7
file content (67 lines) | stat: -rw-r--r-- 2,379 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
The JDK9 javac fixes a bug
([JDK-8058199](https://bugs.openjdk.java.net/browse/JDK-8058199)) that was
causing checkcast instructions to sometimes 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.

This check is a work-around until the Mockito bug is fixed:
[mockito#357](https://github.com/mockito/mockito/issues/357)

The affected answer strategies include:

*   [`RETURNS_DEEP_STUBS`](http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#RETURNS_DEEP_STUBS)
*   [`RETURNS_MOCKS`](http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#RETURNS_MOCKS)
*   [`RETURNS_SMART_NULLS`](http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#RETURNS_SMART_NULLS)

The
[`RETURNS_DEFAULTS`](http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#RETURNS_DEFAULTS)
strategy is usually unaffected, because it returns `null` as the default value
of methods that return `Object`, and unbounded type parameters erase to
`Object`.

## Example:

```java
class Foo {
  <T> T getFirst(Iterable<T> xs) {
    return xs.iterator().next();
  }
}
```

```java
class Test {
  @Mock Foo f;

  @Test
  public void test() {
    Iterable<Boolean> it = Arrays.asList(false);
    when(f.getFirst(it)).thenReturn(false);
  }
}
```

The JDK8 javac would have translated `when(f.getFirst(it))` as:

```
INVOKEVIRTUAL Foo.getFirst (Ljava/lang/Iterable;)Ljava/lang/Object;
INVOKESTATIC org/mockito/Mockito.when (Ljava/lang/Object;)Lorg/mockito/stubbing/OngoingStubbing;
```

The JDK9 javac translates it as:

```
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.