Skip to content

What's new in Mockito 2

Edric Chan edited this page Aug 31, 2021 · 56 revisions

Introduction

This wiki page contains high level narrative about Mockito 2.x development. Comprehensive, fully automated release notes are automatically generated into a separate document: Mockito Release Notes. We use http://shipkit.org to automatically generate beautiful and informative release notes.

Mockito 2.25.0

Mockito 2.25.0 had an important feature added for anyone using mockito-inline. In particular anyone using Kotlin (which demands using mockito-inline) and PowerMock (which exacerbates the problem even more) will want to add this to all of their test classes to avoid a large memory leak:

/**
 * See [Memory leak in mockito-inline...](https://github.com/mockito/mockito/issues/1614)
 */
@After
fun clearMocks() {
    Mockito.framework().clearInlineMocks()
}

Mockito 2.10.0

In Summer 2017 we decided that Mockito should offer better API for advanced framework integrations. The new API is not intended for users who want to write unit tests. It is intended for other test tools and mocking frameworks that need to extend or wrap Mockito with custom logic. During the design and implementation process (#1110) we have improved existing and developed new public API elements.

For details see the Javadoc for main Mockito class. For feedback, please leave comment in issue #1110.

Mockito 2.9.0

Notable changes (all changes):

  • bumped ByteBuddy 1.6.14 -> 1.7.0 (inline mocking, bugfixes)
  • bumped Objenesis 2.5 -> 2.6 (Java9, bugfixes)

Mockito 2.2.x

These are follow up release of minor merged pull request. They can be about fixing, tweaking either behaviour or API, or inner beauty.

  • Added matcher <T> T nullable(Class<T>) that will allow to express a nullable argument of type T
  • Added new mock details API
    • to get mock method invocations :

      Mockito.mockingDetails(a_mock).printInvocations()
    • to get stubs statements (given(...).when(...) / when(...).then(...)) :

      Mockito.mockingDetails(a_mock).getStubbing()

Follow the latest developments in the release note.

Mockito 2.1.0

Even cleaner tests!!! THANK you for writing great tests with us, your patience waiting for version 2, and kudos to fantastic gang of contributors!

Introduction

Mockito 2 demands even cleaner tests: remove unnecessary stubs, detect incorrect code with improved argument matchers, use Java 8 answers, and more. In 2016, Mockito has estimated user base of 2M engineers - thank you very much for mocking with us! We greatly value your feedback, feature requests and contributions: Mockito 2 contains 700 commits from almost 50 individual contributors!!!

Since 2014 Mockito continually delivers: every merged pull request that passes our rigorous unit and integration tests automatically generates: documentation, release notes and artifacts in Maven Central. As much as we are proud our automation and excited about 2.1.0 release we acknowledge our problems: undelivered commitments about the final release date, too many beta versions. We will fix this.

Wondering why 2.1.0 instead of 2.0.0? Read about it on our Continuous Delivery Details page.

The release of Mockito 2 effectively starts Mockito 3 project that focuses on:

  • full-blown Java 8 support, and Java 9 support (depending on the release date)
  • richer stubbing/mocking API (#303)
  • improving & extracting the continuous delivery automation to a standalone library (#608)
  • and more!

Help us! We are eager to expand the team and continue improving unit testing experience with mocks. Submit pull requests! See how to contribute. When your PR gets merged, new version of Mockito is published and it is signed with your name in the release notes!

We hope you will enjoy Mockito 2! Complete release notes are automatically generated and can be found here.

Noteworthy Improvements

  • Changed the mock maker engine from CGLIB to ByteBuddy. This allows Mockito to fix some long standing bugs we had with CGLIB, and pick up byte-code related improvements much quicker (so needed for Java 8 and Java 9). ByteBuddy is a great library and Mockito team is excited to use it now as the default mock-creation engine!

  • To avoid Hamcrest version conflicts that our users reported, Mockito does not depend on Hamcrest anymore. There's still org.mockito.hamcrest.MockitoHamcrest if you need to integrate with Hamcrest.

  • Preliminary Java 8 support.

    Note that Java 8 prior 1.8 Update 45 has serious bugs, that we cannot work around. It is best to use the latest Java 8 versions.

    While Mockito still works with Java 6, it can detect Java 8 runtime and allows

    • to mock an interface with a default method

      interface DM {
          int contract();
          default int default_contract() { return contract() + 1; }
      }
      DM dm = mock(DM.class);
      given(dm.contract()).willReturn(2);
      
      // necessary otherwise default method is stubbed
      given(dm.default_contract()).willCallRealMethod();
      
      assertThat(dm.default_contract()).isEqualTo(3);
    • to return default values for Optional and Stream, respectively java.util.Optional.empty() and java.util.stream.Stream.empty().

  • Mockito JUnit runner and rule can now detect unused stubs. It is possible to revert this behaviour.

    // detect unused stubs
    @RunWith(MockitoJUnitRunner.class)
    
    // don't detect, old behaviour
    @RunWith(MockitoJUnitRunner.Silent.class)

    or with the rule

    // detect unused stubs
    @Rule public MockitoRule mrule = MockitoJUnit.rule();
    
    // don't warn user about misusage, old behaviour
    @Rule public MockitoRule mrule = MockitoJUnit.rule()
                                                 .silent();
  • New API for framework integrators and advanced users: org.mockito.listeners.MockitoListener. We use this API internally to detect unused stubs.

  • Lazy verification is possible with new incubating VerificationCollector Rule:

    @Rule
    public VerificationCollector collector = MockitoJUnit.collector();

    In a test method, all verifications are collected and reported at the end (if there are any failures):

    IMethods methods = mock(IMethods.class);
    // Both methods are not called, but will be reported at once
    verify(methods).simpleMethod();
    verify(methods).byteReturningMethod();

    The default non-lazy way would stop at the verification of simpleMethod and not report the missing verification of byteReturningMethod.

  • Improved generics signatures in several places. That should come handy for matchers especially. These methods are more Java 8 friendly. However note that some matchers that helped with generics for Java prior version 8 are now deprecated and will be removed in Mockito 3.

  • Better support of generic types in deep stubs

    Reminder : deep stubs may signal that a developer is breaking the Law of Demeter

  • Mockito can spy abstract classes (with no argument constructor)

  • Various improvements from mock injection, to variable printing, code cleanup

  • More answers :

    • Introducing Answers.RETURNS_SELF, which should be useful to mock builders
    • Java 8 friendly answers AdditionalAnswers.answer(arg1 -> arg1.toString())

    Explore Mockito and especially AdditionalAnswers to find out the change.

  • Improved BDDMockito API. Better mirror the Mockito stubbing API (when/then) and augment the BDD API as well :

    BDDMockito.then(mock).should(inOrder).doSomething();
    BDDMockito.then(mock).shouldHaveZeroInteractions();
    BDDMockito.then(person).shouldHaveNoMoreInteractions();
  • Added the possibility to access the mock creation settings via

    Mockito.mockingDetails(mock).getMockCreationSettings()
  • A bit more friendliness with JUnit 5 via 3rd party extension. The JUnit team developed a JUnit 5 extension, that allows to inject mocks in the test via method parameters :

    @ExtendWith(MockitoExtension.class)
    class MockitoExtensionInBaseClassTest {
    
      @Mock private NumberGenerator numberGenerator;
      @BeforeEach void set_stubs(@Mock MyType myType, TestInfo testInfo) {
          // do some stubs
      }
    
      @Test
      void firstTestWithInjectedMock(@Mock MyType myType) {
          // play with mock
      }
    }

    The extension repository is here and is not managed by the mockito team. Once JUnit 5 goes live we will look into providing built-in integration (in Mockito).

  • Mockito Continuous Integration and Deployment improvements

Mock the unmockable: opt-in mocking of final classes/methods

For a long time our users suffered a disbelief when Mockito refused to mock a final class. Mocking of final methods was even more problematic, causing surprising behavior of the framework and generating angry troubleshooting. The lack of mocking finals was a chief limitation of Mockito since its inception in 2007. The root cause was the lack of sufficient support in mock creation / bytecode generation. Until Rafael Winterhalter decided to fix the problem and provide opt-in implementation in Mockito 2.1.0. In the releases, Mockito team will make mocking the unmockable completely seamless, greatly improving developer experience.

Mocking of final classes and methods is an incubating, opt-in feature. It uses a combination of Java agent instrumentation and subclassing in order to enable mockability of these types. As this works differently to our current mechanism and this one has different limitations and as we want to gather experience and user feedback, this feature had to be explicitly activated to be available ; it can be done via the mockito extension mechanism by creating the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line:

mock-maker-inline

After you created this file, Mockito will automatically use this new engine and one can do :

final class FinalClass {
  final String finalMethod() { return "something"; }
}

FinalClass concrete = new FinalClass(); 

FinalClass mock = mock(FinalClass.class);
given(mock.finalMethod()).willReturn("not anymore");

assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());

In subsequent milestones, the team will bring a programmatic way of using this feature. We will identify and provide support for all unmockable scenarios. Stay tuned and please let us know what you think of this feature!

For more, take a look at the list of closed issues on GH.

Incompatible changes with 1.10

We tried to minimize the amount of incompatible changes. In order to evolve the library, provide better testing experience, clean up old deprecated code, given the huge adoption of Java 8 and open up for brand new exciting features we need to release a new major version. Incompatible changes like the new anyX() matcher behaviour or detection of unused stubs should actually help with writing cleaner tests!

  • Mockito 2 now requires Java 6 as a minimum.

  • Mockito does not produce the mockito-all artifact anymore ; this one was primarily aimed at ant users, and contained other dependencies. We felt it was time to move on and remove such artifacts as they cause problems in dependency management system like maven or gradle.

  • The Mockito class does not inherit from Matchers class anymore. Instead Mockito and Matchers classes both inherit from ArgumentMatchers. Also Matchers is now deprecated.

  • Matchers.argThat method family had a direct dependency on hamcrest, in order to allow matchers to work without Hamcrest we had to move these API to MockitoHamcrest.

    Change these

    Matchers.argThat()
    Matchers.charThat()
    // similar hamcrest method

    to

    MockitoHamcrest.argThat()
    MockitoHamcrest.charThat()
    // similar hamcrest method
  • InvocationOnMock.getArgumentAt(int,Class) was replaced with InvocationOnMock.getArgument(int), it simplifies the implementation of custom answers.

    Change these

    public Object answer(InvocationOnMock invocation) {
      MyClass first = getArgumentAt(0,MyClass.class);
      ...
    }

    to

    public Object answer(InvocationOnMock invocation) {
      MyClass first = getArgument(0);
      ...
    }
  • anyX() and any(SomeType.class) matchers now reject nulls and check type. This has been long waited feature, making Mockito syntax more intuitive. (In the following list T represents some type like Integer, List)

    • <T> T any() will matches anything including null
    • T anyT() / <T> any(Class<T>) are aliases of <T> isA(T), will matches non-null object of given type. e.g. :
      • int anyInt() won't match null (for Integers) anymore
      • String anyString() won't match null (for Strings) anymore, and will check the object is indeed a String
      • Map<K, V> anyMap() won't match null (for Maps) anymore, and will check the object is indeed a Map
      • <T> List<T> anyListOf(Class<T>) will match non-null List, and will check the object is indeed a List

    Collection type matching are shallow, it means matchers won't verify the type of contained elements, instead it relies on the compiler to warn users on generic misuses.

  • Some public APIs have been tweaked with varargs, this shouldn't affect a lot of users. However for library developers that rely on binary compatibility, it may require a little work.

    BDDMockito.given(mock.invocation()).willThrow(Throwable) // mockito 1.x
    BDDMockito.given(mock.invocation()).willThrow(Throwable...) // mockito 2.x
  • MockMaker now declares a new method isTypeMockable(Class<?>) which must be implemented. Framework integrators were requesting this feature.

  • Deprecated symbols in 1.x have been removed, like

    • MockitoAnnotations.Mock
    • Other internal classes that were previously public like : org.mockito.internal.stubbing.defaultanswers.Answers
  • Various internal changes that may impact library developers

    • Package change
    • Some classes are now static

For more, take a look at the list of incompatible changes on GH.

Some useful regular expressions for migration with Intellij and Eclipse:

  • replace import org.mockito.runners.MockitoJUnitRunner; with import org.mockito.junit.MockitoJUnitRunner;
  • replace @RunWith(MockitoJUnitRunner.class) with @RunWith(MockitoJUnitRunner.Silent.class) (Only for a softer migration. Should be switched back to non silent mode after everything compiles)
  • replace import (.*) org.mockito.(Mockito|Matchers).argThat; with import $1 org.mockito.hamcrest.MockitoHamcrest.argThat;
  • replace (\w+).getArgumentAt[(]([a-zA-Z0-9]*),\s*(.*?).class[)]\. with (($3)$1.getArgument($2)).
  • replace .getArgumentAt[(]([a-zA-Z0-9]*),.*?[)] with .getArgument($1)

Under the hood

Mockito switched from CGLIB to the great library ByteBuddy to make mocks. This change allows Mockito to make better mocks (like copying the annotations of the mocked type). Globally switching to ByteBuddy fixed a number of issues we couldn't fix with CGLIB and globally improved mocks behaviour.

If you don't know the project go to the project's repository. It has been coded by Rafael Winterhalter, one of the core Mockito developers. He spent a lot of time making byte-code manipulations robust and easy to use. Thanks Rafael!

Due to bugs present in early Java 8 VMs, mockito can only work on up to date JVMs, e.g.
  • Minimum Oracle Hotspot working version is 1.8.0u45
  • It has been reported that early IBM J9 have issues, as IBM version scheme is a bit confusing and as we don't have access to all J9 versions we can't pinpoint a minimum specific version, what we know is that version R28_jvm.28_20150630_1742_B255633 don't work but version R28_Java8_SR3_20160719_1144_B312156 is fine. (See #801)

Project activities

Tim Van Der Lippe set up a waffle, which is a board that connects to the GitHub issues / pull requests APIs and as such allows to track and organise activities on the project.

The page is available here : https://waffle.io/mockito/mockito

Team changes

The team has changed quite a bit and 3 core developers have joined the project:

  • Rafael Winterhalter
  • Tim Van Der Lippe
  • Marcin Zajączkowski

Special thanks

Many contributors outside the team greatly helped the project and deserve a special mention, Thanks guys!

  • Christian Schwarz for PR, reviews and code
  • Pascal Schumacher for PR, reviews and code
  • Éric Lefevre-Ardant for his long running and continuous support on the mailing-list

See all contributors.