pact-foundation / pact-jvm

JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
https://docs.pact.io
Apache License 2.0
1.09k stars 480 forks source link

4.2.x+ Upgrade Issues:- org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [au.com.dius.pact.consumer.MockServer mockServer] #1492

Closed divyaapaduvalli closed 4 months ago

divyaapaduvalli commented 2 years ago

I am figuring out how to implement consumer driven contract testing using pact junit5. But the test keeps failing because of no parameter resolver for the injected MockServer, even though the test class is extended with PactConsumerTestExt. My understanding is the parameter resolver for MockServer should be provided with PactConsumerTestExt extension. Would be great if anyone could help me out here!!

Java version : 11

Spring boot version : 2.6.1

Pact library used :

<dependency>
    <groupId>au.com.dius.pact.consumer</groupId>
    <artifactId>junit5</artifactId>
    <version>4.3.2</version>
    <scope>test</scope>
</dependency>

My consumer pact test class with one interaction :

import au.com.dius.pact.consumer.MockServer;
import au.com.dius.pact.consumer.dsl.PactDslJsonBody;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt;
import au.com.dius.pact.consumer.junit5.PactTestFor;
import au.com.dius.pact.consumer.junit5.ProviderType;
import au.com.dius.pact.core.model.RequestResponsePact;
import au.com.dius.pact.core.model.annotations.Pact;
import com.divyaa.consumer.beans.Product;
import com.divyaa.consumer.services.ProductService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith(PactConsumerTestExt.class)
public class AddProductPact {

    @Pact(provider = "ProviderService", consumer = "ConsumerService")
    public RequestResponsePact createPact(PactDslWithProvider builder){
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json; charset=utf-8");

        PactDslJsonBody body = new PactDslJsonBody()
                .stringType("id","10")
                .stringType("type", "CREDIT_CARD")
                .stringType("name", "Gem Visa");
        return builder
                .given("Product doesn't exist")
                .uponReceiving("Add product")
                    .path("/products")
                    .method("POST")
                .willRespondWith()
                    .status(200)
                    .headers(headers)
                    .body(body)
                .toPact();
    }

    @Test
    @PactTestFor(providerName = "ProviderService", providerType = ProviderType.ASYNCH)
    public void example(MockServer mockServer){
        Product expectedProduct = new Product();
        expectedProduct.setId("10");
        expectedProduct.setType("CREDIT_CARD");
        expectedProduct.setName("Gem Visa");
        List<Product> expected = Arrays.asList(expectedProduct);

        String baseUrl = mockServer.getUrl();
        RestTemplate restTemplate = new RestTemplateBuilder()
                .rootUri(baseUrl)
                .build();
        Product product = new ProductService(restTemplate).addProduct(expectedProduct);

        assertEquals(expected, product);
    }
}

The above test fails with following error :

org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [au.com.dius.pact.consumer.MockServer mockServer] in method [public void AddProductPact.example(au.com.dius.pact.consumer.MockServer)].

    at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameter(ExecutableInvoker.java:200)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameters(ExecutableInvoker.java:183)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameters(ExecutableInvoker.java:144)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:96)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

I noticed I was using org.junit.Test instead of org.junit.jupiter.api.Test, but got the same error even after changing.

Tried using ParametrizedTest, but my test got ignored.

Connected to the target VM, address: '127.0.0.1:61332', transport: 'socket'

Test ignored.
11:10:22.545 [main] INFO au.com.dius.pact.consumer.junit5.PactConsumerTestExt - Writing pacts out to default directory
francislainy commented 2 years ago

I had this issue this week. I think you'll need to set the version to still use version 3 on your @pactFor method.

@Test
   @PactTestFor(providerName = PACT_PROVIDER, port = "8080", pactVersion = PactSpecVersion.V3)

and not sure you should keep the ProviderType.ASYNCH part unless you're testing for messages.

mikrethor commented 2 years ago

I have a similar issue with the httprequest. Tell me if you prefer that I open a specific one.

I get the following error org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [org.apache.http.HttpRequest arg1] in method [void somepackage.pact.PactControllerProviderIT.pactVerificationTestTemplate(au.com.dius.pact.provider.junit5.PactVerificationContext,org.apache.http.HttpRequest)].

This is my pact dependencies in the pom.xml file. `

au.com.dius.pact.consumer
        <artifactId>junit5</artifactId>
        <version>4.3.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>au.com.dius.pact.provider</groupId>
        <artifactId>junit5spring</artifactId>
        <version>4.3.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>au.com.dius.pact.core</groupId>
        <artifactId>matchers</artifactId>
        <version>4.3.2</version>
        <scope>test</scope>
    </dependency>`

And the test failing : `import au.com.dius.pact.provider.junit5.HttpTestTarget; import au.com.dius.pact.provider.junit5.PactVerificationContext; import au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider; import au.com.dius.pact.provider.junitsupport.Provider; import au.com.dius.pact.provider.junitsupport.State; import au.com.dius.pact.provider.junitsupport.StateChangeAction; import au.com.dius.pact.provider.junitsupport.VerificationReports; import au.com.dius.pact.provider.junitsupport.loader.PactBroker; import au.com.dius.pact.provider.junitsupport.loader.PactBrokerAuth; import com.fasterxml.jackson.databind.ObjectMapper; import somepackage.security.JWTAutoConfiguration; import somepackage.TestUtils; import lombok.SneakyThrows; import org.apache.http.HttpRequest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles;

import java.util.List;

@VerificationReports(value = {"console", "json", "markdown"}, reportDir = "target/pacts/reports") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureWireMock(port = 0) @Import({JWTAutoConfiguration.class}) @ActiveProfiles("it") @Provider("provider-replaced") @PactBroker( authentication = @PactBrokerAuth(token = "${PACT_BROKER_AUTH_TOKEN}"), url = "https://someurl/") class PactControllerProviderIT {

@LocalServerPort
private int port;

private final String bearerToken = String.format("Bearer %s", TestUtils.getEncodedJWT("pact-test"));
protected boolean userAuthorized;
private static final String PACT_MATCHING_WILDCARD = "pact.matching.wildcard";

@BeforeAll
static void beforeAll() {
    System.setProperty(PACT_MATCHING_WILDCARD, "true");
}

@AfterAll
static void afterAll() {
    System.clearProperty(PACT_MATCHING_WILDCARD);
}

@BeforeEach
void beforeEach(PactVerificationContext context) {
    HttpTestTarget target = new HttpTestTarget("localhost", port);
    context.setTarget(target);
    userAuthorized = true;
}

@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context, HttpRequest request) {
    if (userAuthorized) {
        request.addHeader("Authorization", bearerToken);
    }

    context.verifyInteraction();
}

}`

Prior to using the version 4.3.2 I was using a 4.0.6 version

mikrethor commented 2 years ago

It's working fine using 4.2.17

francislainy commented 2 years ago

@mikrethor I think you may need to update your http client too. <http.client.version>5.1.2</http.client.version> as pact 4.3.x uses http client 5.x

           <dependency>
            <groupId>org.apache.httpcomponents.core5</groupId>
            <artifactId>httpcore5</artifactId>
            <version>${http.client.version}</version>
        </dependency>
divyaapaduvalli commented 2 years ago

I had this issue this week. I think you'll need to set the version to still use version 3 on your @pactFor method.

@Test
@PactTestFor(providerName = PACT_PROVIDER, port = "8080", pactVersion = PactSpecVersion.V3)

and not sure you should keep the ProviderType.ASYNCH part unless you're testing for messages.

That worked! Thank you! And yeah, ProviderType.ASYNCH is not required in my case.

mikrethor commented 2 years ago

I am gonna try but I don't want to override the specific version which comes with my spring-boot-dependencies 👍 +- io.rest-assured:rest-assured:jar:4.3.3:test | +- org.codehaus.groovy:groovy:jar:3.0.9:test | +- org.codehaus.groovy:groovy-xml:jar:3.0.9:test | +- org.apache.httpcomponents:httpclient:jar:4.5.13:test | | +- org.apache.httpcomponents:httpcore:jar:4.4.14:test | | - commons-codec:commons-codec:jar:1.15:test | +- org.apache.httpcomponents:httpmime:jar:4.5.13:test

johnreilly100 commented 2 years ago

@mikrethor im seeing the same error as you. Did you get a fix for it.

francislainy commented 2 years ago

I am gonna try but I don't want to override the specific version which comes with my spring-boot-dependencies 👍 +- io.rest-assured:rest-assured:jar:4.3.3:test | +- org.codehaus.groovy:groovy:jar:3.0.9:test | +- org.codehaus.groovy:groovy-xml:jar:3.0.9:test | +- org.apache.httpcomponents:httpclient:jar:4.5.13:test | | +- org.apache.httpcomponents:httpcore:jar:4.4.14:test | | - commons-codec:commons-codec:jar:1.15:test | +- org.apache.httpcomponents:httpmime:jar:4.5.13:test

I'm on 4.4.0 for RestAssured but I'm pretty confident it would work for 4.4.3 too.

mikrethor commented 2 years ago

I reverted to 4.2 for now but I am gonna try to fix the version in my project to see if the last version work

johnreilly100 commented 2 years ago

I was able to get around this issue by removing HttpRequest as a parameter of the @TestTemplate annotated method. Of course that's no good to anyone who needs to modify the request before its sent.

francislainy commented 2 years ago

I was able to get around this issue by removing HttpRequest as a parameter of the @TestTemplate annotated method. Of course that's no good to anyone who needs to modify the request before its sent.

Which version of http request are you using? It should work okay with <http.client.version>5.1.2</http.client.version>

johnreilly100 commented 2 years ago

org.apache.httpcomponents.core5:httpcore5:jar:5.1.1

mikrethor commented 2 years ago

Yeah in my case I need it to modify the header.

hedshogg commented 2 years ago

If you're using the @TestTemplate parameter HttpRequest from au.com.dius.pact.provider version 4.3.x check that the import is org.apache.hc.core5.http.HttpRequest because the old org.apache.http.HttpRequest version was replaced, see the Pact JUnit 5 Extension docs.

timordenewitz commented 2 years ago

If you're using the @TestTemplate parameter HttpRequest from au.com.dius.pact.provider version 4.3.x check that the import is org.apache.hc.core5.http.HttpRequest because the old org.apache.http.HttpRequest version was replaced, see the Pact JUnit 5 Extension docs.

Thanks @hedshogg that solved it for me!

nagkumar commented 2 years ago

https://github.com/pact-foundation/pact-jvm/issues/1309

When moving from testImplementation 'au.com.dius.pact.consumer:junit5:4.1.39' to testImplementation 'au.com.dius.pact.consumer:junit5:4.2.0' the dependency of testImplementation 'au.com.dius.pact.consumer:junit5:4.1.39 of testImplementation 'au.com.dius.pact.consumer:java8:4.1.39can be removed by changing the imports

e.g these imports in the code

import static io.pactfoundation.consumer.dsl.LambdaDsl.newJsonArrayMinLike;
import static io.pactfoundation.consumer.dsl.LambdaDsl.newJsonBody;

can be changed to

import static au.com.dius.pact.consumer.dsl.LambdaDsl.newJsonArrayMinLike;
import static au.com.dius.pact.consumer.dsl.LambdaDsl.newJsonBody;
Alfagun74 commented 1 year ago

If you're using the @TestTemplate parameter HttpRequest from au.com.dius.pact.provider version 4.3.x check that the import is org.apache.hc.core5.http.HttpRequest because the old org.apache.http.HttpRequest version was replaced, see the Pact JUnit 5 Extension docs.

That was it for me too. Hoooooly shit thank you...

YOU54F commented 4 months ago

Closing this now as this is pretty searchable on google, and the workarounds provided help users migrate to 4.2.x+

I ended up here, because it was quicker to google the error in my terminal than read the docs. We are all guilty!

As ever thanks to the posters who provided their input for things that worked. You guys make the world go round 💛