JMockit Expectation for constructor with List parameters

84 Views Asked by At

Have no issues to mock constructor with non-list parameter but I cannot figure out how to mock constructor with List parameters in Expectations section.

Tested code:

 public Mono<ParameterObject> create(UUID id, List<Foo> foos) {
        return Mono.just(foos)
                   .zipWith(barRepository.findSomething(id).collectList())
                   .map(function((foos, bars)-> new ParameterObject(foos, bars)));
    }

Test:

 @Test
    void testMe(@Injectable ParameterObject parameterObject) {
        var foos = List.of(new Foo());
        var bar = new Bar();

        new Expectations(ParameterObject.class) {{
            barRepository.findBySomething(ID);
            result = Flux.just(bar);

            new ParameterObject(foos, List.of(bar));
            //new ParameterObject(foos, (List<Bar>) any)); // also won't work - still different instances` references
            //new ParameterObject((List<Foo) any, (List<Bar>) any)); // also won't work - still different instances` references
            result = parameterObject;
        }};

        StepVerifier.create(subject.create(ID, foos))
                    .expectNext(parameterObject)
                    .verifyComplete();
    }

Result:

expected value: com.package.ParameterObject@662773f8; 
actual value: com.package.ParameterObject@1c375a0b

As we might see there is difference in ParametObject instances` references. It means our ParameterObject constructor mock wasn't even invoked.

What I do wrong here?

1

There are 1 best solutions below

0
Jeff Bennett On

You're going down a weird road in your expectations block where it looks like you are trying to mock the constructor when certain parameters are supplied. Mocking constructors is something that can be done, but it's tricky and has special syntax. See the docs if you really want to go that way.

That said, there are 2 easier solutions. 1.) mock "create(..)" to just return parameterObject. Since you are trying to test the behavior of create(..), this is not for you, but may work in other situations. 2.) In your class-under-test, extract the "new ParameterObject(..)" piece of the lambda to a separate method, such that the lambda calls the "new"-method and the "new"-method does the actual new. Have 1 test that tests the lambda (with the "new"-method mocked) and a 2nd test that just tests the 'new'-method

 public Mono<ParameterObject> create(UUID id, List<Foo> foos) {
    return Mono.just(foos)
               .zipWith(barRepository.findSomething(id).collectList())
               .map(function((foos, bars)-> construct(foos,bars)));
}
public void construct(List<Foo> foos, List<Bar> bars) {
    new ParameterObject(foos, bars)
}

 @Test
void testMe(@Injectable ParameterObject parameterObject) {
    var foos = List.of(new Foo());
    var bar = new Bar();

    new Expectations(ParameterObject.class) {{
        barRepository.findBySomething(ID);
        result = Flux.just(bar);

        construct((List<Foo) any, (List<Bar>) any));
        result = parameterObject;
    }};

    StepVerifier.create(subject.create(ID, foos))
                .expectNext(parameterObject)
                .verifyComplete();
}