Giter Site home page Giter Site logo

mockito-java8's Introduction

Mockito-Java8 Build Status Maven Central

Mockito add-ons leveraging Java 8 and lambda expressions to make mocking even more compact.

Quick start

Lambda matcher

Allows for stubbing with matcher logic defined within a lambda expression. Useful when working with complex classes pass as an argument.

given(ts.findNumberOfShipsInRangeByCriteria(
    argLambda(c -> c.getMinimumRange() > 1000))).willReturn(4);

Argument Captor - Java 8 edition

Allows to use ArgumentCaptor in one line (here with AssertJ):

verify(ts).findNumberOfShipsInRangeByCriteria(
    assertArg(sc -> assertThat(sc.getMinimumRange()).isLessThan(2000)));

Mockito API methods available via interfaces (without static imports)

Allows to use methods from Mockito API without the need to use static imports. It is enough to make your test class implement WithBDDMockito interface to have all methods from Mockito API available directly (especially useful for Eclipse users).

class SpaceShipTest implements WithBDDMockito {
    //stub and verify as usual without static imports
}

Available interfaces: WithBDDMockito, WithMockito and WithAdditionalMatchers.

Configuration in a project

mockito-java8 jars are available in Maven Central.

Gradle

testCompile 'info.solidsoft.mockito:mockito-java8:2.5.0'

Maven

<dependency>
    <groupId>info.solidsoft.mockito</groupId>
    <artifactId>mockito-java8</artifactId>
    <version>2.5.0</version>
    <scope>test</scope>
</dependency>

Other

Click Maven Central badge Maven Central to get configuration snippets for SBT, Ivy and more.

Mockito compatibility

Mockito-Java8 has two development lines. Versions 1.x (and 0.3.x) should be compatible with Mockito 1.10.12+ and 2.0.x-beta up to 2.0.21-beta. Versions 2.x supports the new matchers API and should be compatible with Mockito 2.0.22-beta and newer versions.

Documentation for versions 1.x can be found in a separate branch.

Provided add-ons

Lambda matcher

Allows to define matcher logic within a lambda expression. Useful when working with complex classes pass as an argument.

@Immutable
class ShipSearchCriteria {
    int minimumRange;
    int numberOfPhasers;
}
@Test
public void shouldAllowToUseLambdaInStubbing() {
    //given
    given(ts.findNumberOfShipsInRangeByCriteria(
        argLambda(c -> c.getMinimumRange() > 1000))).willReturn(4);
    //expect
    assertThat(ts.findNumberOfShipsInRangeByCriteria(new ShipSearchCriteria(1500, 2)))
        .isEqualTo(4);
    //expect
    assertThat(ts.findNumberOfShipsInRangeByCriteria(new ShipSearchCriteria(700, 2)))
        .isEqualTo(0);
}

In comparison the same logic implemented with a custom Answer in Java 7:

@Test
public void stubbingWithCustomAnswerShouldBeLonger() {  //old way
    //given
    given(ts.findNumberOfShipsInRangeByCriteria(any())).willAnswer(new Answer<Integer>() {
        @Override
        public Integer answer(InvocationOnMock invocation) throws Throwable {
            Object[] args = invocation.getArguments();
            ShipSearchCriteria criteria = (ShipSearchCriteria) args[0];
            if (criteria.getMinimumRange() > 1000) {
                return 4;
            } else {
                return 0;
            }
        }
    });
    //expect
    assertThat(ts.findNumberOfShipsInRangeByCriteria(new ShipSearchCriteria(1500, 2)))
        .isEqualTo(4);
    //expect
    assertThat(ts.findNumberOfShipsInRangeByCriteria(new ShipSearchCriteria(700, 2)))
        .isEqualTo(0);
}

Even Java 8 and less readable constructions don't help too much:

@Test
public void stubbingWithCustomAnswerShouldBeLongerEvenAsLambda() {  //old way
    //given
    given(ts.findNumberOfShipsInRangeByCriteria(any())).willAnswer(invocation -> {
        ShipSearchCriteria criteria = (ShipSearchCriteria) invocation.getArguments()[0];
        return criteria.getMinimumRange() > 1000 ? 4 : 0;
    });
    //expect
    assertThat(ts.findNumberOfShipsInRangeByCriteria(new ShipSearchCriteria(1500, 2)))
        .isEqualTo(4);
    //expect
    assertThat(ts.findNumberOfShipsInRangeByCriteria(new ShipSearchCriteria(700, 2)))
        .isEqualTo(0);
}

Argument Captor - Java 8 edition

Allows to use ArgumentCaptor in one line:

@Test
public void shouldAllowToUseAssertionInLambda() {
    //when
    ts.findNumberOfShipsInRangeByCriteria(searchCriteria);
    //then
    verify(ts).findNumberOfShipsInRangeByCriteria(
        assertArg(sc -> assertThat(sc.getMinimumRange()).isLessThan(2000)));
}

In comparison to 3 lines in the classic way:

@Test
public void shouldAllowToUseArgumentCaptorInClassicWay() {  //old way
    //when
    ts.findNumberOfShipsInRangeByCriteria(searchCriteria);
    //then
    ArgumentCaptor<ShipSearchCriteria> captor = 
        ArgumentCaptor.forClass(ShipSearchCriteria.class);
    verify(ts).findNumberOfShipsInRangeByCriteria(captor.capture());
    assertThat(captor.getValue().getMinimumRange()).isLessThan(2000);
}

Mockito API methods available via interfaces (without static imports)

Allows to use methods from Mockito API without the need to use static imports. It is enough to make your test class implement WithBDDMockito interface to have all methods from stubbing/mockito Mockito API available directly.

//no need to use static imports!

public class SpaceShipTest implements WithBDDMockito {

    @Test
    public void shouldVerifyMethodExecution() {
        //given
        TacticalStation tsSpy = spy(TacticalStation.class);
        willDoNothing().given(tsSpy).fireTorpedo(2);
        //when
        tsSpy.fireTorpedo(2);
        tsSpy.fireTorpedo(2);
        //then
        then(tsSpy).should(times(2)).fireTorpedo(2);
    }
}

The same code would work fine with a bunch of static imports. Of course they can be hidden in IDE and usually do not disturb much. Nevertheless to be able to write just a method name (e.g. mock(TacticalStation.class)) without a class is it required to press ALT-ENTER (in IntelliJ IDEA) to add each static import on the first usage of a given method in a test class. The situation is even worse in Eclipse where it is required to earlier add BDDMockito to "Favorites" in "Content Assist" to make it suggested by IDE.

Mockito methods are provided by 3 base interfaces, being an entry point for given set of methods:

Rationale

Mockito-Java8 is a side effect of my short presentation Java 8 brings power to testing! which I gave at GeeCON TDD 2015 and DevConf.cz 2015. In my speech, using 4 examples, I showed how Java 8 - namely lambda expressions - can simplify testing tools.

Limitations

Unfortunately there is a set of nice Java 8 related features which cannot be implemented without a non backward compatible changes in Mockito core and therefore cannot be implemented as an add-on. A good news is that Mockito 3.0 is planned to require Java 8 making it all possible.

Java 9 compatibility

The project is automatically tested with Java 9 (and Java 10) in the CI environment. At least the base scenarios should work with Java 9. Feel free to report an issue if you encounter any mockito-java8 specific problem.

The project's JAR artifact contains an Automatic-Module-Name manifest attribute. It's value - info.solidsoft.mockito.mockito-java8 - is used as the name of the automatic module defined by that JAR file when it is placed on the Java 9 module path. This allows to explicitly require mockito-java8 in other projects.

The mockito-java8 dependencies itself will be declared explicitly once available in required projects.

Additional information

mockito-java8 has been written by Marcin Zajączkowski. The author can be contacted directly via email: mszpak ATT wp DOTT pl. There is also Marcin's blog available: Solid Soft - working code is not enough.

mockito-java8 is a separate project and is NOT supported by The Mockito Core Team.

The library is licensed under the terms of the Apache License, Version 2.0.

mockito-java8's People

Contributors

dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar ferada avatar szpak avatar szpak-ci avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mockito-java8's Issues

Include OSGi metadata so that the jar is a valid bundle

I'd like to use this in some OSGi tests - as such it would be easier to use if this was a valid bundle (i.e. had suitable data in the MANIFEST.MF file). I'm not familiar with Gradle - otherwise I would submit a pull request.

Disable mockito-core dependency range

Mockito 2 is still in beta and the range in mockito-java8 transitive dependencies resolve the latest mockito-core version (which could be not compatible and need to be excluded). The fixed version should be better as newer version defined in the end project should override that from mockito-java8.

Lambda in assertArg called twice

We have found an interesting issue, that seems to be caused by assertArg. For the following test

    verify(mockedService).functionThatExpectsAnInputStream(assertArg((InputStream is) -> {
            try {
                // Oh my god this thing gets invoked twice !! 
                assertThat(IOUtils.toByteArray(is),is("a,b,c".getBytes()));
            } catch (IOException e) {
                fail();
            }
        }));

The lambda block gets invoked twice, being the IS in the second execution empty.

However if the same logic is done using a regular ArgumentCaptor, it works as expected (a single execution is done):

        ArgumentCaptor<InputStream> is = ArgumentCaptor.forClass(InputStream.class);
        verify(mockedService).functionThatExpectsAnInputStream(is.capture());
        assertThat(is.getValue(),notNullValue());
        assertThat(IOUtils.toByteArray(is.getValue()),is("a,b,c".getBytes()));

We could provide further context if needed.

Thank you very much.

assertArg should allow lambda to throw checked exceptions

When testing code like this (combining mockito + assertJ + mockito-java8), with some assertJ JSON psuedo-code:

    verify(myObject.someMethod(assertArg(s -> {
        assertThat(s).isJSON();
        assertThat(Jackson.parseJSON(s)).hasProperty("foo");   // throws JSONParseException
    });

If Jackson.parseJSON throws a checked exception, the compiler wants me to catch it inside the lambda. However, that exception should just fail the test, since like any other exception, it means something unexpected has gone wrong.

Hence, I suggest assertArg be widened to take a CheckedConsumer<T> type:

    @FunctionalInterface
    interface CheckedConsumer<T> {
        void accept(T value) throws Throwable;
    }

Runtime exceptions for AssertionMatcher if used with Java 8u60 and 8u65

mockito-java8 stopped working with Java 8u60+ (8u65 is the latest available at the time of writing).

Errors observed in mockito-java8 build:

java.lang.NullPointerException
    at info.solidsoft.mockito.java8.AssertionMatcherTest.shouldAllowToUseAssertionInLambdaWithPrimitiveAsArgument(AssertionMatcherTest.java:53)
org.mockito.exceptions.misusing.UnfinishedVerificationException: 
Missing method call for verify(mock) here:
-> at info.solidsoft.mockito.java8.AssertionMatcherTest.shouldAllowToUseAssertionInLambdaWithPrimitiveAsArgument(AssertionMatcherTest.java:53)
org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced argument matcher detected here:

-> at info.solidsoft.mockito.java8.AssertionMatcher.assertArg(AssertionMatcher.java:78)

It happens with Java 8u60 and 8u65 (both OracleJDK and OpenJDK). With 8u51 (and for example 8u31 on Travis) it works fine. Tested with Mockito 1.10.19 and 2.0.21-beta (mockito-java8 is not compatible with newer Mockito versions).

Full stacktraces: https://gist.github.com/szpak/f85fb33f6d27aa66ccf5.
Branch reproducing the problem: https://github.com/szpak/mockito-java8/tree/tech/strangeTestFailure
Failing Travis build with 8u66: https://travis-ci.org/szpak/mockito-java8/builds/90568711

Provide support for new matcher API in mockito 2.0.22+

The Mockito team refactored Matcher hierarchy to decouple from Hamcrest. mockito-java8 has to be rewritten to support the new matcher API to not fail with the following error in Mockito 2.0.22+:

java.lang.IncompatibleClassChangeError: class info.solidsoft.mockito.java8.AssertionMatcher has interface org.mockito.ArgumentMatcher as super class

It would break compatibility with Mockito 1.10.x and <2.0.22.

Default answers for Java 8 specific classes

Additional Answer supporting smart default values Java 8 specific classes (like empty for Optional).

Could be used during a mock creation mock(MyJava8Class.class, RETURNS_JAVA8_EMPTY_VALUES) or just mock8(MyJava8Class.class).

Unfortunately due to immutable specifics of enum - Answers cannot be extended from the 3rd-party library and it would not be possible to use it with @Mock annotation. As a workaround it could be used as a default answer for the whole project using IMockitoConfiguration mechanism. See my old blog post for details.

Classes to consider:

  • Optional
  • CompletableFuture

New Mockito release removed deprecated classes.

Just noticed due to a failing build: The newest Beta release of Mockito (2.0.54-beta) removed some deprecated classes, c.f. this commit and breaks this package with at least DeprecatedOngoingStubbing (that's the one I've noticed in the logs).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.