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

Adding @QuarkusTest support for PactVerifyProvider #1506

Open
the1stflag opened this issue Jan 18, 2022 · 7 comments
Open

Adding @QuarkusTest support for PactVerifyProvider #1506

the1stflag opened this issue Jan 18, 2022 · 7 comments
Labels
bug Indicates an unexpected problem or unintended behavior

Comments

@the1stflag
Copy link

Hi,

we are currently evaluating pact and try to setup MessageTestTarget on provider side. We are using Quarkus injection. However pact and Quarkus don´t work together currently in this scenario. When starting the test our code will be injected, but when the @PactVerifyProvider method will be called pact seems to invoke and use a new instance of the test class instead of using the existing one. So all our injections are null.

@PactBroker
@QuarkusTest
@Provider("myProvider")
public class PactAsyncProviderTest {

    @Inject
    MySnsService service;

    @InjectMock
    SnsClient snsClient;

    ArgumentCaptor<PublishRequest> captor = ArgumentCaptor.forClass(PublishRequest.class);


    @TestTemplate
    @ExtendWith(PactVerificationInvocationContextProvider.class)
    void testTemplate(Pact pact, Interaction interaction, PactVerificationContext context) {
        context.verifyInteraction();
    }

    @BeforeEach
    void before(PactVerificationContext context) {
        context.setTarget(new MessageTestTarget());
    }

    @PactVerifyProvider("doSomething")
    MessageAndMetadata doSomething() {
            SomeData someData = SomeData.builder()
                    .valueA("someValue")
                    .valueB("someOtherValue")
                    .build();
            service.publish(someData);
            Mockito.verify(snsClient).publish(captor.capture());
            return new MessageAndMetadata(captor.getValue().message().getBytes(), Maps.newHashMap());
    }
}

The test is successful if we remove Quarkus injection.

@lostiniceland
Copy link

lostiniceland commented Dec 20, 2022

I can confirm this issue with a HttpTestTarget on the latest version 4.4.2

The instance of the Provider when it is running through the BeforeEach is a different one than when the code (later) enters the State method.
In BeforeEach the injects are available, but when entering the state-method all fields are nulled (of course, since it is not the same instance).

I assume this issue is on Quarkus side, since it does its compile-time magic in order to startup the app for QuarkusTest

@rholshausen rholshausen added the bug Indicates an unexpected problem or unintended behavior label Jan 12, 2023
@rholshausen
Copy link
Contributor

/jira ticket

@github-actions
Copy link

👋 Thanks, Jira [PACT-546] ticket created.

@rholshausen
Copy link
Contributor

This is a QuarkusTest issue. I took the https://github.com/quarkusio/quarkus-quickstarts/tree/main/amazon-sns-quickstart example and added a similar test as yours above, and I can see the following while running the test with a debugger.

In the beforeEach extension callback, the JUnit5 context.requiredTestInstance is returning

context.requiredTestInstance = {PactAsyncProviderTest@16540} 
 service = null
 snsClient = null
 captor = {ArgumentCaptor@16541} 

with the injected fields null.

Then in the test @BeforeEach method,

this = {PactAsyncProviderTest@16586} 
 service = {QuarksCannonSyncResource@16587} 
 snsClient = {SnsClient$MockitoMock$yGTiPFTZ@16588} "Mock for SnsClient, hashCode: 1464233911"
 captor = {ArgumentCaptor@16589} 

This is the same for the actual test run with the beforeTestExecution extension callback and the test testTemplate method.

beforeTestExecution:

context.requiredTestInstance = {PactAsyncProviderTest@16540} 
 service = null
 snsClient = null
 captor = {ArgumentCaptor@16541} 

testTemplate:
this = {PactAsyncProviderTest@16586}
service = {QuarksCannonSyncResource@16587}
snsClient = {SnsClient$MockitoMock$yGTiPFTZ@16588} "Mock for SnsClient, hashCode: 1464233911"
captor = {ArgumentCaptor@16589}

Compare this with a normal jUnit 5 Pact test, you see that context.requiredTestInstance returns the same instance as for the test methods.

In the beforeEach extension callback:

context.requiredTestInstance = {AmqpContractTest@5190} 

Then in the test @BeforeEach method:

this = {AmqpContractTest@5190} 

So it looks like something from the QuarkusTest is setting a different instance for the JUnit 5 test instance.

@rholshausen
Copy link
Contributor

Having a very brief look at the QuarkusTest source code, I think what is happening is that QuarkusTest is intercepting all the JUnit 5 method calls and setting up appropriate state. But it won't know about any of the Pact specific method calls, so those are probably running with the original test instance and not the QuarkusTest setup one.

@daniel-alonso-sanchez
Copy link

Any updates on this? I'm facing a similar issue :(

Thank you so much in advance!

@holly-cummins
Copy link
Contributor

@rholshausen, I can confirm your analysis, although the changes being done by the Quarkus framework extend past state to things like CDI bean injection.

I think this will be fixed by quarkusio/quarkus#34681. The best Quarkus experience will come from using the Quarkus Pact extensions, and for Quarkus 3+ the extensions are needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Indicates an unexpected problem or unintended behavior
Projects
None yet
Development

No branches or pull requests

5 participants