4

I need to check an api of the type /meeting/id using Pact tests and REST Assured. The id may change and I'd like to create an item before the test and inject their id to overwrite what is set as part of the url path for the contract but not sure how to go about that please?

Here my consumer:

@ExtendWith(PactConsumerTestExt.class)
public class PactConsumerTest {

Map<String, String> headers = new HashMap<>();


String storeMeetingPath = "/meeting/256";

@Pact(provider = VC, consumer = ED_UI)
public RequestResponsePact createPact(PactDslWithProvider builder) {

    headers.put("Content-Type", "application/json");
    headers.put("Accept", "application/json");

    return builder
            .given("A request to retrieve a meeting for a user")
            .uponReceiving("A request to retrieve a meeting for a user")
            .path(storeMeetingPath)
            .method("GET")
            .headers(headers)
            .willRespondWith()
            .body(new PactDslJsonBody()
                    .integerType("meetingId", 3)
                    .stringType("meetingTitle", "My title")
                    .stringType("meetingDescription", "My description"))
            .status(200)
            .toPact();
}


@Test
@PactTestFor(providerName = VC, port = "8080")
public void runTest() {

    //Mock url
    RestAssured.baseURI = "http://localhost:8080";
    RequestSpecification rq = RestAssured
            .given()
            .headers(headers)
            .when();

    rq.get(storeMeetingPath);

}

}

And here my provider:

@Provider(VC)
@PactFolder("target/pacts")
public class PactProviderTest {

@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void pactTestTemplate(PactVerificationContext context, HttpRequest request) {
    request.addHeader("Authorization", AUTHORIZATION_TOKEN);
    context.verifyInteraction();
}

@BeforeEach
void before(PactVerificationContext context) {
    context.setTarget(new HttpsTestTarget(getBasePactEndpoint(), 443, "/"));

    // Will create a meeting and retrieve the id from here but how to use this to overwrite what is there on the consumer?       

    getAuthorizationToken(UserType.TEACHER);
}

@State("A request to retrieve a meeting for a user")
public void sampleState() {

}

}

Thank you very much.

Francislainy Campos
  • 3,462
  • 4
  • 33
  • 81

1 Answers1

7

I've learned the answer for this and here it is in case it may be helpful to anyone else:

Replace this line on the consumer contract

.path(storeMeetingPath)

With

.pathFromProviderState("/meeting/${id}", "/meeting/500") // 500 is just a sample for the data type to be inferred. It can be any value from that same type.

So that we'd have a template with a default value.

And change this on provider side

@State("A request to retrieve a meeting for a user")
public void sampleState() {

} 

To have this instead, so that it returns a map which would set the key and value for the element to be injected.

@State("A request to retrieve a meeting for a user")
public Map sampleState() {

    Map<String, Integer> map = new HashMap<>();
    map.put("id", 391); // id for the meeting I want to retrieve.

    return map;
}

Auxiliar documentation:

Consumer: https://github.com/DiUS/pact-jvm/tree/master/consumer#having-values-injected-from-provider-state-callbacks

Provider: https://github.com/DiUS/pact-jvm/tree/master/provider/junit#returning-values-that-can-be-injected

Francislainy Campos
  • 3,462
  • 4
  • 33
  • 81
  • Hi Francislainy! Can you share also how did you solve the problem of running successfully runTest() in your consumer? In the original problem you used the following path with RestAssured: "/meeting/256". With the proposed solution the new path is "/meeting/${id}". The HTTP client needs a resolved path in order to perform the GET against the mock server. Using the default value of the template generates a 500 status code as response. Thanks in advance! – Nahuel Dalla Vecchia May 27 '21 at 20:57
  • Hi, you're injecting the id here ```@State("A request to retrieve a meeting for a user") public Map sampleState() { Map map = new HashMap<>(); map.put("id", 391); return map; }``` so it resolves to a valid path. – Francislainy Campos May 28 '21 at 02:17
  • Yes, that works for the provider tests. What about the consumer tests? Ie. `rq.get(storeMeetingPath);` from runTest()? You can declare @State in the consumer, but RestAssure will be unable to resolve storeMeetingPath=/meeting/${id}, /meeting/{id} with pathParam 500, or even /meeting/500. – Nahuel Dalla Vecchia May 28 '21 at 13:51
  • instead of this ```return builder .given("A request to retrieve a meeting for a user") .uponReceiving("A request to retrieve a meeting for a user") .path(storeMeetingPath) .method("GET") .headers(headers) .toPact();``` – Francislainy Campos May 28 '21 at 17:07
  • you have this ```return builder .given("A request to retrieve a meeting for a user") .uponReceiving("A request to retrieve a meeting for a user") .pathFromProviderState(storeMeetingPathWithAppendedId, "your path with appended id") .method("GET") .headers(headers) .status(200) .toPact();``` – Francislainy Campos May 28 '21 at 17:08
  • Ah! I used the sample path, and now it worked! Thanks for your quick response! :-) – Nahuel Dalla Vecchia May 28 '21 at 19:28
  • No worries, glad it's working for you now. :) – Francislainy Campos May 30 '21 at 06:35