jmockit/jmockit1

[question] how to mock reactive stack

bilak opened this issue · 10 comments

bilak commented

Please provide the following information:

  • Version of JMockit that was used: 1.49

  • Description of the problem or enhancement request:
    I'm trying to mock the results of some repository methods. These methods are returning reactive (Mono/Flux) objects. When I mock more methods in one class all returns the first mock result. I don't understand what is the problem here.

The test sample is here

  • Check the following:
  • If a defect or unexpected result, JMockit project members should be able to reproduce it.
    For that, include an example test (perhaps accompanied by a Maven/Gradle build script) which
    can be executed without changes and reproduces the failure.

  • If an enhancement or new feature request, it should be justified by an example test
    demonstrating the validity and usefulness of the desired enhancement or new feature.

  • The issue does not fall outside the scope of the project (for example, attempting to use
    JMockit APIs from Groovy or Scala code, or with an Android runtime).

  • The JDK where the problem occurs is a final release, not a development build.

It is a bug. I tested it with @Capturing DemoRepositoryImpl demoRepository and it works without any problem. So I tried to add times =1 into the Expectations.

new Expectations() {{
  demoRepository.testMethodOne();
  result = Mono.just("monoone");
  times = 1;

  demoRepository.testMethodTwo();
  result = Mono.just("monotwo");
  times = 1;
}};

and changed an implementation in the method callMonoMethods to:

@Override
public String callMonoMethods() {
  Mono<String> one = demoRepository.testMethodOne();
  Mono<String> two = demoRepository.testMethodTwo();
  return Mono.zip(one, two)
    .map(tuple -> tuple.getT1() + tuple.getT2())
    .block();
}

and then an exception is thrown:

Unexpected invocation to:
com.example.demo.data.repository.CustomDemoRepository#testMethodOne()

but it is from a call of testMethodTwo

Can't reproduce the failure without the code under test.

bilak commented

Can't reproduce the failure without the code under test.

@rliesenfeld What you mean by that? Did you run the tests and they are working properly?

Ah ok, now I see all the code is under https://github.com/bilak-poc/jmockit-reactive.

I get org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [com.example.demo.data.repository.DemoRepository demoRepository] in method [void com.example.demo.DemoApplicationTests.testMono(com.example.demo.data.repository.DemoRepository)].

Which, of course, occurs because JMockit isn't getting initialized, due to missing configuration in the pom.xml.

bilak commented

let me fix the example, maybe I just copied something wrong from our application

With a simplified version of the code which still uses the Reactive API (Mono), it works fine for me:

final class DemoApplicationTests {
    @Tested TestService testService;
    @Injectable DemoRepository demoRepository;

    @Test
    void testMono() {
        new Expectations() {{
            demoRepository.getOne(); result = Mono.just("monoone");
            demoRepository.getTwo(); result = Mono.just("monotwo");
        }};

        var result = testService.callMonoMethods();

        assertEquals("monoonemonotwo", result);
    }
}

@Service
public final class TestService {
    @Autowired private DemoRepository demoRepository;

    public String callMonoMethods() {
        return Mono.zip(demoRepository.getOne(), demoRepository.getTwo())
            .map(tuple -> tuple.getT1() + tuple.getT2())
            .block();
    }
}

public interface DemoRepository {
    Mono<String> getOne();
    Mono<String> getTwo();
}
bilak commented

@rliesenfeld I've fixed the config. Why then it's not working with my configuration? We are using mainly integration tests where we are trying to use @Capturing parameter only for some objects through method parameter. If we use @Capturing as a test class field then we have other problems.

I think the problem is with combination of Spring Data and JMockit. Spring Data generates Method implementation by JDK proxy.

Yes, it seems related to bytecode generated by the Spring framework. And @Capturing does not apply to dynamically generated classes.

In this case, the goal was to mock Repository interfaces in integration tests. I suggest to not mock them and just create actual integration tests.