-
-
Notifications
You must be signed in to change notification settings - Fork 482
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
Can't get provider to recognize matchers in pact #1604
Comments
Hi - I know I only created this late last week, but it is somewhat time sensitive. @holly-cummins and I are doing a talk at Devoxx Belgium in a couple of weeks (https://devoxx.be/talk/?id=6661) and we wanted to use the Quarkus Sample application (https://github.com/quarkusio/quarkus-super-heroes) as a demo for "showing off" Pact as well as its integration with pactflow.io. This issue is a blocker for that. Neither she nor I can figure out how to get the provider tests to pass using the matchers specified in the Pact generated from the consumers. Any help would be greatly appreciated! |
However, Pact understands how to match content-type headers, so you don't need to use Coming to the body, most of the "type" matchers will only work with structured documents (like JSON and XML). Think about what you are trying to so. You body is set as The only matcher that works with plain text is the regex one. Remember the regex will need to match the entire body contents. You may also want to consider using a JSON body instead of plain text. |
Thank you for the comments. I will give it a try tomorrow. Unfortunately I can't change the APIs. They are what they are. So for matching a text/plain body my matcher should essentially be |
That will match everything except the empty string, looking at your example you could also try something like |
But then wouldn't that make it so the consumer understood what the provider's test data was? In a real world situation I'm not sure the consumer would know what text to expect other than "some text". The consumer has no control over what the provider might send back (and honestly, this consumer really doesn't care as long as it's something. I'm trying to be as loose as possible and not have the provider and consumer not know too much about each other, other than the contract. |
That makes sense, essentially you are stating the body must not be empty. |
Thank you. I will play with this more in the morning! |
@rholshausen I did get the body to validate using the regex!
I'm not sure I quite understand? If I don't use In my consumer test I do @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()
.headers(Map.of(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))
.status(Status.OK.getStatusCode())
.body(PactDslRootValue.stringMatcher(".+", DEFAULT_HELLO_RESPONSE))
.toPact(V4Pact.class);
} 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": "regex",
"regex": ".+"
}
]
}
}
},
"status": 200
},
"type": "Synchronous/HTTP"
}
],
"metadata": {
"pact-jvm": {
"version": "4.3.14"
},
"pactSpecification": {
"version": "4.0"
}
},
"provider": {
"name": "rest-villains"
}
} When I verify on the provider I get [from Pact Broker https://quarkus-super-heroes.pactflow.io/pacts/provider/rest-villains/consumer/rest-fights/pact-version/e7b9f3ef4a9a6651d81db46e546d2b19902a75d5/metadata/c1tdW2JdPW1haW4mc1tdW2xdPXRydWUmc1tdW2N2XT0xNg]
A hello request
Test Name: io.quarkus.sample.superheroes.fight.client.VillainConsumerContractTests.helloVillains()
Comments:
07:48:19 DEBUG [io.qu.sa.su.vi.re.VillainResource] (executor-thread-0) Hello Villain Resource
returns a response which
has status code 200 (OK)
has a matching body (OK) It doesn't seem to do any kind of verification on the Or do you mean to say that if the |
I think i've moved on past the "hello world" one to something a little more complicated. I have a I've gotten the provider to verify the @State("No random villain found")
public void clearData() {
} It doesn't seem, though, that I have access to any of the beans in my application when that method fires. I tried to use mocking and I also tried to inject my application's datasource as a CDI bean and that doesn't seem to work either. Any class-level attribute that I've injected into my test class seems to be When I go to run the tests I end up with a |
I was actually able to get this work by doing some stuff in the @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
}
} |
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:
It is generated by a JUnit 5 test (
au.com.dius.pact.consumer:junit5:4.3.14
)The pact is published to pactflow.io.
On the provider side I have this (
au.com.dius.pact.provider:junit5:4.3.14
):When I run the verification I'm getting this:
It looks like its doing exact matching on the header and body, even though the pact has matching rules where
Content-Type
header should containtext/plain
, not be equal totext/plain
String
and shouldn't be trying to verify an exactString
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 ofPactSpecVersion.V4
but got the same result.The entire source code of the consumer & provider is available:
The text was updated successfully, but these errors were encountered: