-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Draft Mockito 5 release notes
These release notes will be put on the GitHub release, similar to what we did with Mockito 4: https://github.com/mockito/mockito/releases/tag/v4.0.0
For a while now, we have seen an increase in problems/incompatibilities with recent versions of the JDK. Most notably, JDK 17 made some changes which are incompatible with the current subclass mockmaker. Therefore, to prepare for the future of JDK, we are making some core changes to ensure Mockito keeps on working.
Back in Mockito 2.7.6, we published a new mockmaker based on the "inline bytecode" principle. This mockmaker creates mocks by generating bytecode equivalent to the original class and in its method implementations hooks into the normal Mockito machinery. As a comparison, the subclass mockmaker generates "real" subclasses of mocks, to mimic the same behavior. While the approaches are similar, the inline mockmaker avoids certain restrictions that the JDK imposes. For example, it does not violate module boundaries (introduced in JDK 9, but more heavily used in JDK 17) and avoids the leaking of the creation of the subclass.
Massive thanks to community member @reta who implemented this change.
There are legitimate remaining use cases for the subclass mockmaker. For example, on the Graal VM, the inline mockmaker will not work and the subclass mockmaker is the appropriate choice. Additionally, if you would like to avoid mocking final classes, using the subclass mockmaker is a possibibility. Note however that if you solely want to use the subclass mockmaker to avoid mocking final, you will run into the above mentioned issues on JDK 17+. We want to leave this choice up to our users, which is why we will keep on supporting the subclass mockmaker.
If you want to use the subclass mockmaker instead, you can use the new mockito-subclass artifact (published on Maven Central along with all our other artifacts).
Mockito 4 supports Java 8 and above.
Similar to other open source projects, we are moving away from JDK 8 and to newer versions.
The primary reason for moving away from JDK 8 is the increasing maintenance costs with keeping our own infrastructure working.
Lately we have been running into more and more JDK 8 breakages.
Additionally, while we want to support the newest JDK API's, our current solution to support both JDK 8 and newer versions causes issues with the SecurityManager.
Since we want Mockito to work on the newest version and more and more businesses adopting JDK 11, we have decided to make the switch as well.
Massive thanks to community member @reta who implemented this change.
For JDK 8 and below, you can keep on using Mockito 4. This is similar to if you are using JDK 6, for which you can keep on using Mockito 2. The changes in Mockito 5 (for now) are primarily focused on the latest JDK versions, which means the API differences between Mockito 4 and 5 are minimal. However, over time this will most likely widen, so we do recommend adopting JDK 11 in the future.
One of our most used public API's for customizing Mockito is the ArgumentMatcher interface.
The interface allows you to define a custom matcher, which you can pass into method arguments to provide more targeted matches.
One major shortcoming of the ArgumentMatcher was the lack of varargs support.
There were many, many issues filed related to varargs and Mockito unable to handle them.
Community member @big-andy-coates put in a lot of effort to come up with an appropriate solution, including fully implementing and comparing 2 approaches.
Ultimately, we decided that introducing a new type() method on ArgumentMatcher is the best solution.
As a result, it is now possible to update your custom matchers to implement varargs support, if you so desire.
Note that ArgumentMatcher is still a @FunctionalInterface and can therefore still be written as a lambda.
Massive thanks to community member @big-andy-coates who implemented this change.
Previously, there were various stubbing scenarios that were unable to be declared with Mockito.
For example, imagine you want to match exactly 2 Strings passed into a method that accepts varargs.
Since Mockito did not know whether you wanted to match the whole array or the individual arguments, when you specified a matcher it would always opt to match the fully array:
Mockito.when(good.call(Mockito.any(String.class))).thenReturn(1L);
good.call("First String", "Second String");The above would match, as any would be marked as "can handle varargs" and thus change its behavior depending on whether it was used in a position that would accept varargs.
However, this would cause problems when there was another overload method (which is legal in JDK 8+) that only accepts a single argument:
// Can never match on this, as Mockito always would use an `Array` if there is a varargs method
Long call(String value);
// Mockito would always choose this one
Long call(String... values);Now, with the new type() method, the internal implementation of any() can differentiate between the two.
If you want to match on a single String, you can pass in its type like you would before.
However, if you want to match on an String[] (and thus match all its arguments), you can do so too:
// Matches invocations to `Long call(String value);`
Mockito.when(good.call(Mockito.any(String.class))).thenReturn(1L);
// Matches invocations to `Long call(String... values);`
Mockito.when(good.call(Mockito.any(String[].class))).thenReturn(1L);Therefore, if you want to match 0 or more arguments, use any(String[].class).
If you want to only match 1 argument, use any(String.class).
In a similar fashion, the behavior of ArgumentCaptor.forClass has changed as well.
If you want to capture all arguments, use an ArgumentCaptor for String[], otherwise String:
// Will capture 1 string
@Captor private ArgumentCaptor<String> captor;
// Will capture all strings
@Captor private ArgumentCaptor<String[]> captor;For more information, see the description and conversation in pull request 2835 and pull request 2807.
No, you don't need to.
Mockito 5 declares a default implementation, returning Void.type as the type of an ArgumentMatcher.
This essentially means that Mockito will not consider the type when handling varargs.
However, if you do return a specific type, Mockito will consider this when matching arguments.
As a result, this new method is not a source-breaking change, but is a bytecode-breaking change.
All code working on Mockito 4 should work as-is when recompiled with Mockito 5.