question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Can't get provider to recognize matchers in pact

See original GitHub issue

This could totally be user error as I’m somewhat new to this, but for the life of me I can not figure out how to get the provider to verify a pact without it trying to do exact matching.

I have a consumer which generates this pact:

{
  "consumer": {
    "name": "rest-fights"
  },
  "interactions": [
    {
      "comments": {
        "testname": "io.quarkus.sample.superheroes.fight.client.VillainConsumerContractTests.helloVillains()",
        "text": [

        ]
      },
      "description": "A hello request",
      "key": "2ec6e2e8",
      "pending": false,
      "request": {
        "headers": {
          "Accept": [
            "text/plain"
          ]
        },
        "method": "GET",
        "path": "/api/villains/hello"
      },
      "response": {
        "body": {
          "content": "Hello villains!",
          "contentType": "text/plain",
          "contentTypeHint": "DEFAULT",
          "encoded": false
        },
        "headers": {
          "Content-Type": [
            "text/plain"
          ]
        },
        "matchingRules": {
          "body": {
            "$": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            }
          },
          "header": {
            "Content-Type": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "regex",
                  "regex": "text/plain"
                }
              ]
            }
          }
        },
        "status": 200
      },
      "type": "Synchronous/HTTP"
    }
  ],
  "metadata": {
    "pact-jvm": {
      "version": "4.3.14"
    },
    "pactSpecification": {
      "version": "4.0"
    }
  },
  "provider": {
    "name": "rest-villains"
  }
}

It is generated by a JUnit 5 test (au.com.dius.pact.consumer:junit5:4.3.14)

@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(
  providerName = "rest-villains",
  pactVersion = PactSpecVersion.V4,
  hostInterface = "localhost",
  // Make an assumption and hard-code the Pact MockServer to be running on port 8081
  // I don't like it but couldn't figure out any other way
  port = "8081"
)
public class VillainConsumerContractTests {
  @Pact(consumer = "rest-fights")
  public V4Pact helloPact(PactDslWithProvider builder) {
    return builder
      .uponReceiving("A hello request")
        .path(VILLAIN_HELLO_URI)
        .method(HttpMethod.GET)
        .headers(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN)
      .willRespondWith()
        .matchHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN, MediaType.TEXT_PLAIN)
        .status(Status.OK.getStatusCode())
        .body(PactDslRootValue.stringType(DEFAULT_HELLO_RESPONSE))
      .toPact(V4Pact.class);
  }

  @Test
  @PactTestFor(pactMethod = "helloPact")
  void helloVillains() {
    runHelloVillains(); // What this does is unimportant here. It works and generates the pact pasted above
  }
}

The pact is published to pactflow.io.

On the provider side I have this (au.com.dius.pact.provider:junit5:4.3.14):

@Provider("rest-villains")
@PactBroker(url = "https://myname.pactflow.io")
@EnabledIfSystemProperty(named = "pactbroker.auth.token", matches = ".+", disabledReason = "pactbroker.auth.token system property not set")
public class ContractVerificationTests {
  @TestTemplate
  @ExtendWith(PactVerificationInvocationContextProvider.class)
  void pactVerificationTestTemplate(PactVerificationContext context) {
    context.verifyInteraction();
  }

  @BeforeEach
  void beforeEach(PactVerificationContext context) {
    context.setTarget(new HttpTestTarget("localhost", 8081));
  }

  @PactBrokerConsumerVersionSelectors
  public static SelectorBuilder consumerVersionSelectors() {
    return new SelectorBuilder()
      .branch(System.getProperty("pactbroker.consumer.branch", "main"));
  }
}

When I run the verification I’m getting this:

Verifying a pact between rest-fights (1.0) and rest-villains

  Notices:
    1) The pact at https://myname.pactflow.io/pacts/provider/rest-villains/consumer/rest-fights/pact-version/ed93e91f94fe8ea4404bb1c08318b77005f1f432 is being verified because the pact content belongs to the consumer version matching the following criterion:
    * latest version from branch 'main' (1.0)

  [from Pact Broker https://myname.pactflow.io/pacts/provider/rest-villains/consumer/rest-fights/pact-version/ed93e91f94fe8ea4404bb1c08318b77005f1f432/metadata/c1tdW2JdPW1haW4mc1tdW2xdPXRydWUmc1tdW2N2XT0xNg]
  A hello request

  Test Name: io.quarkus.sample.superheroes.fight.client.VillainConsumerContractTests.helloVillains()

  Comments:

08:35:29 DEBUG [io.qu.sa.su.vi.re.VillainResource] (executor-thread-0) Hello Villain Resource
    returns a response which
      has status code 200 (OK)
      includes headers
        "Content-Type" with value "text/plain" (FAILED)
      has a matching body (FAILED)

Failures:

1) Verifying a pact between rest-fights and rest-villains - A hello request includes headers "Content-Type" with value "[text/plain]"

    1.1) header: Expected 'text/plain;charset=UTF-8' to match 'text/plain'

    1.2) body: / Expected body 'Hello villains!' to match 'Hello Villain Resource' using equality but did not match

It looks like its doing exact matching on the header and body, even though the pact has matching rules where

  1. The Content-Type header should contain text/plain, not be equal to text/plain
  2. The body should match any String and shouldn’t be trying to verify an exact String

Am I doing something wrong here or missing something? Or is something not working the way it should?

On the consumer I also tried using PactSpecVersion.V3 instead of PactSpecVersion.V4 but got the same result.

The entire source code of the consumer & provider is available:

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:10 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
rholshausencommented, Sep 20, 2022

That makes sense, essentially you are stating the body must not be empty.

0reactions
edeandreacommented, Sep 20, 2022

I was actually able to get this work by doing some stuff in the @BeforeEach method. At that point the CDI context is fully available.

@QuarkusTest
@Provider("rest-villains")
@PactBroker(url = "https://quarkus-super-heroes.pactflow.io")
@EnabledIfSystemProperty(named = "pactbroker.auth.token", matches = ".+", disabledReason = "pactbroker.auth.token system property not set")
public class ContractVerificationTests {
  @ConfigProperty(name = "quarkus.http.test-port")
  int quarkusPort;

  @InjectSpy
  VillainService villainService;

  @TestTemplate
  @ExtendWith(PactVerificationInvocationContextProvider.class)
  void pactVerificationTestTemplate(PactVerificationContext context) {
    context.verifyInteraction();
  }

  @BeforeEach
  void beforeEach(PactVerificationContext context) {
    context.setTarget(new HttpTestTarget("localhost", this.quarkusPort));

    var isNoRandomVillainFoundState = Optional.ofNullable(context.getInteraction().getProviderStates())
      .orElseGet(List::of)
      .stream()
      .filter(state -> "No random villain found".equals(state.getName()))
      .count() > 0;

    if (isNoRandomVillainFoundState) {
      when(this.villainService.findRandomVillain())
        .thenReturn(Optional.empty());
    }
  }

  @PactBrokerConsumerVersionSelectors
  public static SelectorBuilder consumerVersionSelectors() {
    return new SelectorBuilder()
      .branch(System.getProperty("pactbroker.consumer.branch", "main"));
  }

  @State("No random villain found")
  public void clearData() {
    // Already handled in beforeEach
  }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Matching | Pact Docs
Generally speaking, it doesn't matter what value the provider actually returns during verification, as long as the types match. When you need ...
Read more >
PACT jvm matching rules are being ignored when running test
1 Answer 1 ... The problem is with your content-type header: "Content-Type": "text/json;charset=utf-8" . The proper content type for JSON payloads ...
Read more >
Advanced Contract Testing - Pact Verification with Pattern ...
Let's see how to use pattern matching and query params with Pact ... response we get during contract validation will be an exact...
Read more >
@pact-foundation/pact - npm
Read Getting started with Pact for more information for beginners. Pact JS ... Array contains matcher; Provider State Injected Values.
Read more >
Contract testing with PactJS and Jest | by Jun Wei Ng | Medium
That is highly undesired, as the provider shouldn't be constrained by its consumers. This is where Pact Matchers can help. Pact Matchers allow ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found