Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible incompatibility with Spring context, JUnit5, pact provider async and maven #1265

Open
albertominetti opened this issue Dec 7, 2020 · 10 comments

Comments

@albertominetti
Copy link

albertominetti commented Dec 7, 2020

I recently upgraded all my pact provider tests to JUnit5 using au.com.dius.pact.provider:junit5:4.1.11. For the async ones I am getting this error:

2020-12-07 16:11:08.212  INFO [xenon-test,,,] [main][KafkaProducerPact] The following profiles are active: test
2020-12-07 16:11:09.765  INFO [xenon-test,,,] [main][KafkaProducerPact] Started KafkaProducerPact in 9.663 seconds (JVM running for 69.654)

Verifying a pact between order-entry-service and order-capture-service for tag CI
  [from Pact Broker http://hostt:32908/pacts/provider/provider-service/consumer/consumer-service/version/1.4.0 (Tag CI)]
  New order
    generates a message which
      Verification Failed - Failed to invoke provider method 'provideOrder'
Failures:
1) New order
    1.1)       Failed to invoke provider method 'provideOrder'

Before, using JUnit4, they were working with IDEA and with maven.
Now, using JUnit5, they work only when running with IDEA, but there is no way to have them working with using maven.

I am using with failsafe and surefire plugin, I tried with the combination of the following configuration, but the result is always failure.

            <useSystemClassLoader>false</useSystemClassLoader>
            <useManifestOnlyJar>false</useManifestOnlyJar>

Here a sample of the code:

@Provider("provider-service")
@Consumer("consumer-service")
@PactBroker
@SpringBootTest
@TestPropertySource("classpath:pact.properties")
public class KafkaProducerPact {

    @Autowired
    private Jackson2ObjectMapperBuilder builder;

    @TestTemplate
    @ExtendWith(PactVerificationSpringProvider.class)
    public void verifyInteraction(PactVerificationContext context) {
        context.verifyInteraction();
    }

    @BeforeEach
    public void before(PactVerificationContext context) {
        context.setTarget(new MessageTestTarget(of(this.getClass().getPackage().getName())));
    }

    @PactVerifyProvider("New order")
    public String provideOrder() throws JsonProcessingException {
        return provideNewOrder();
    }

    private String provideNewOrder() throws JsonProcessingException {
        NewOrder order = createNewOrderForProducerPact();
        return builder.build().writeValueAsString(order);
    }
}

As you can see I need the Spring context only for retrieving the actual ObjectMapper. I tried using @JsonTest , that only load the ObjectMapper without the whole application, but I got the same result.

Without the Spring context (neither @SpringBootTest neither @JsonTest neither @WebfluxTest, etc) everything works, except it is not using the actual ObjectMapper.

Seems that there is some incompatibility between Spring context, JUnit5, failsafe/surefire and pact jvm provider for async messages. I suppose it is related to the class loader because in IDEA works.

@albertominetti
Copy link
Author

albertominetti commented Dec 7, 2020

I tried to reproduce the issue using a fork from your original pact-maven-amqp-test. I added Spring, but it seems to work properly. Here is mine with only one commit.

Do you have an idea how can I get more details from the logs?

@albertominetti
Copy link
Author

albertominetti commented Dec 7, 2020

I tried to insert the example from the github repo in the source code of the real application, the behavior is the following: everything works from IDEA, but not from maven.

I tought it may be related to some dependency, but I cannot find any difference, here the versions I am using:

spring-boot: 2.3.6.RELEASE (and also 2.4.0)
pact-jvm: 4.1.11
maven: 3.6.3
surefire: 2.22.2 (and 3.0.0-M4)

Moreover I found this strange behavior:
image

In the first println the mapper is not null, while in the second it is null!

2020-12-07 23:32:24.488  INFO [main][UserPactPact] Started UserPactPact in 46.506 seconds (JVM running for 101.105)
NOT NULLLLLLL com.fasterxml.jackson.databind.ObjectMapper@243cc85c
2020-12-07 23:32:24.761  INFO [xenon-test,,,] [main][PactVerificationStateChangeExtension] Invoking state change method 'SomeProviderState':SETUP

Verifying a pact between test_consumer and user-service
  [Using File C:\...-service\target\test-classes\pacts_x\test-consumer-user-service.json]
  Given SomeProviderState
  user created message
    generates a message which
NULLLLLLLLLLL null
      Verification Failed - Invalid Json document (1:2) - found only whitespace characters
 
Failures:
1) user created message
    1.1)       Invalid Json document (1:2) - found only whitespace characters

Seems the context is removed.

@albertominetti
Copy link
Author

albertominetti commented Dec 9, 2020

Seems the issue is related to the forkCount for the maven plugin (surefire or failsafe). Here is the failing maven build because of the forkCount = 0. You could probably include this requirements in the docs.

@uglyog
Copy link
Member

uglyog commented Dec 12, 2020

So to understand this issue, when forking is disabled with Maven surefire, the Spring injected values are null during execution of the verification method, but not null during the execution of the before method?

I need to investigate what the forkCount=0 is actually doing. What JVM version are you using?

@albertominetti
Copy link
Author

Exaclty, I use two different JVM:

java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

and

java version "15" 2020-09-15
Java(TM) SE Runtime Environment (build 15+36-1562)
Java HotSpot(TM) 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)

@uglyog
Copy link
Member

uglyog commented Dec 27, 2020

Looks like the issue is that when forkCount == 0 it is getting a new instance of the test class that doesn't have the fields autowired:

---------------------------------------------
BeforeEach -> mapper=com.fasterxml.jackson.databind.ObjectMapper@5d4af473
BeforeEach -> this=com.github.uglyog.mavenamqp.UserPactTest@486dd616
---------------------------------------------

---------------------------------------------
State -> mapper=com.fasterxml.jackson.databind.ObjectMapper@5d4af473
State -> this=com.github.uglyog.mavenamqp.UserPactTest@486dd616
---------------------------------------------

---------------------------------------------
PactVerifyProvider -> mapper=null
PactVerifyProvider -> this=com.github.uglyog.mavenamqp.UserPactTest@77a13162
---------------------------------------------

@uglyog
Copy link
Member

uglyog commented Dec 27, 2020

The interesting thing is that during the provider state callback, it is correct. They should have similar invocation mechanism, so I would have expected the behavior to be the same.

uglyog pushed a commit that referenced this issue Dec 27, 2020
@uglyog
Copy link
Member

uglyog commented Dec 27, 2020

The issue is that the forkCount == 0 setting is resulting in a different class loader being used to load the method to call. The same Java class loaded with different class loaders are not equivalent.

I have applied a fix for that. The other option is to not specify the class paths to scan, and it would have used the default class loader.

@uglyog
Copy link
Member

uglyog commented Dec 28, 2020

4.1.13 released

@albertominetti
Copy link
Author

Hello, after the upgrade to 4.1.13, it still fails. Here the sample project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants