Injecting test data into dependent services before validating your PACT.json against the provider by using pact-jvm

1,382 views
Skip to first unread message

Alex Verbitchi

unread,
Mar 5, 2015, 3:28:27 PM3/5/15
to pact-s...@googlegroups.com
Hi everyone,

What it will be the way to implement setUp and tearDown in order to create test data (by using simple REST calls) in a depended service when we validate the PACT json file against the provider service.

For example we have a Consumer service and the Provider which has a couple of dependencies to other services (Service-A and Service-B). Provider-service retrieve some data from Service-A and Service-B and store this data in another service .


So in order to create the consumer tests I'm using Pact JUnit Rule, and everything is fine when I'm running my tests agains the provider where all dependent services are mocked and return the expected result to my provider service.

But when I'm trying to validate my generated pact file consumer-provider.json against an up and running real provider service, the validation of the pact fails, cause the required data is missing in services Service-A and Service-B.

In order to validate pact file against provider, I'm using maven plugin pact-jvm-provider-maven.

What approach should I use in order to create my test-data before I validate the pact. There is any way to create some setUp and tearDown facts, which will create test data in dependent services through simple REST calls?

Thanks,

Alex




Ronald Holshausen

unread,
Mar 5, 2015, 5:12:31 PM3/5/15
to Alex Verbitchi, pact-s...@googlegroups.com
Hi Alex,

Pact defines a Provider State which is a description of the state the provider must be in for the interaction. You define the state in your consumer test, and then when running the verification of the provider you can provide a state change URL to call to setup anything required for that test. The state description will be passed via the call.

Refer to the following section of the maven plugin: https://github.com/DiUS/pact-jvm/tree/master/pact-jvm-provider-maven#provider-states

For my projects (I use Dropwizard), we have an endpoint available when run with the test classpath when we are running the pact verification. This URL loads fixture data based on the provider state that is passed in to it. I use the same fixture data in my consumer tests for the responses from the mock server.

Regards,

Ron

P.S. Nice diagram.



--
You received this message because you are subscribed to the Google Groups "Pact" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pact-support...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Ronald Holshausen

DiUS Computing Pty Ltd

Level 10, 99 Queens Street
Melbourne, VIC 3000

Phone: +61 3 9008 5400
Mobile: +61 413 162 439

http://www.diuscomputing.com.au

Alex Verbitchi

unread,
Mar 8, 2015, 11:39:58 AM3/8/15
to pact-s...@googlegroups.com, shury...@gmail.com
Hi Ronald,

Thanks for your answer. 

Just starting to play with Pact and I still struggle to create a good picture how should I use the stateChangeUrl from the pact-jvm maven plugin.

- URL to use to switch the state of the provider
- This URL will receive the providerState description from the pact file before each interaction via a POST.



I created a new state in consumer tests: @Pact(state = "book a car with setup", provider = "Provider", consumer = "Consumer"). 

what will be my stateChangeUrl? 
is it one of the mocked URL? or, as you mentioned it's an endpoint which loads test data for you?
How to define and pass the state description?

Also a question:
There is any way to parameterize the json pact file, and to inject some variables? For example in my case I need to inject the IDs of the documents created in dependent services during the setUp.


My consumer test:
class ServiceConsumerTest {

def DATA_A_ID = "AAAAAAAA_ID";
def DATA_B_ID = "BBBBBBBB_ID";


def headers = ["Content-Type" : "application/json;charset=UTF-8"]


@Rule
public PactRule rule = new PactRule("localhost", 1234, this);


@Pact(state = "book a car", provider = "Provider", consumer = "Consumer")
public PactFragment configurationFragment(ConsumerPactBuilder.PactDslWithProvider.PactDslWithState builder) {

return builder
.uponReceiving("retrieve data from Service-A")
.path("/persons/${DATA_A_ID}")
.method("GET")
.willRespondWith()
.headers(headers)
.status(200)
.body("{\t\n" +
"\t\"firstName\":\"John\",\n" +
"\t\"lastName\":\"Smith\"\n" +
"}")

.uponReceiving("retrieving data from Service-B")
.path("/cars/${DATA_B_ID}")
.method("GET")
.willRespondWith()
.headers(headers)
.status(200)
.body("{\t\n" +
"\t\"brand\":\"Honda\",\n" +
"\t\"model\":\"Civic\",\n" +
"\t\"year\": 2012\n" +
"}")

.uponReceiving("book a car")
.path("/orders/")
.method("POST")
.body("{\n" +
"\tperson: {\n" +
"\t\"id\" : \"AAAAAA_ID\",\t\t\n" +
"\t\"firstName\":\"John\",\n" +
"\t\"lastName\":\"Smith\"\n" +
"\t}\n" +
"\n" +
"\tcar:\t{\n" +
"\t\"id\":\"BBBBBB_ID\"\t\n" +
"\t\"brand\":\"Honda\",\n" +
"\t\"model\":\"Civic\",\n" +
"\t\"year\": 2012\n" +
"\t}\n" +
"}")
.willRespondWith()
.headers(headers)
.status(201)
.body("{\n" +
"\t\"id\": \"ORDER_ID_XXXXX\"\n" +
"}")
.toFragment();
}

@Pact(state = "book a car with setup", provider = "Provider", consumer = "Consumer")
public PactFragment configurationFragment(ConsumerPactBuilder.PactDslWithProvider.PactDslWithState builder) {


//TODO SETU_UP ????
// Create person and person_ID
// Create cart and get car_ID

return builder
.uponReceiving("retrieve data from Service-A")
.path("/persons/${DATA_A_ID}")
.method("GET")
.willRespondWith()
.headers(headers)
.status(200)
.body("{\t\n" +
"\t\"firstName\":\"John\",\n" +
"\t\"lastName\":\"Smith\"\n" +
"}")

.uponReceiving("retrieving data from Service-B")
.path("/cars/${DATA_B_ID}")
.method("GET")
.willRespondWith()
.headers(headers)
.status(200)
.body("{\t\n" +
"\t\"brand\":\"Honda\",\n" +
"\t\"model\":\"Civic\",\n" +
"\t\"year\": 2012\n" +
"}")

.uponReceiving("book a car")
.path("/orders/")
.method("POST")
.body("{\n" +
"\tperson: {\n" +
"\t\"id\" : \"AAAAAA_ID\",\t\t\n" +
"\t\"firstName\":\"John\",\n" +
"\t\"lastName\":\"Smith\"\n" +
"\t}\n" +
"\n" +
"\tcar:\t{\n" +
"\t\"id\":\"BBBBBB_ID\"\t\n" +
"\t\"brand\":\"Honda\",\n" +
"\t\"model\":\"Civic\",\n" +
"\t\"year\": 2012\n" +
"\t}\n" +
"}")
.willRespondWith()
.headers(headers)
.status(201)
.body("{\n" +
"\t\"id\": \"ORDER_ID_XXXXX\"\n" +
"}")
.toFragment();
}


@PactVerification("book a car")
@Test
void "testBookCar"()
{
Response response =  providerRestClient.placeOrder("http://localhost:8080/orders/"),  DATA_A_ID, DATA_B_ID, "2015-03-15");

Assert.assertEquals(200, response.getStatusCode());
Assert.assertNotNull(response.get("orderId"));
}
}


Part of my pom file:
<plugin>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-provider-maven_2.11</artifactId>
<version>2.1.9</version>
<configuration>
<serviceProviders>
<!-- You can define as many as you need, but each must have a unique name -->
<serviceProvider>
<name>checkout-provider</name>
<!-- All the provider properties are optional, and have sensible defaults (shown below) -->
<stateChangeUrl>
?????

</stateChangeUrl>
<protocol>http</protocol>
<host>localhost</host>
<port>8080</port>
<!--<path>/</path>-->
<requestFilter>
<!--request.addHeader-->
</requestFilter>
<consumers>
<!-- Again, you can define as many consumers for each provider as you need, but each must have a unique name -->
<consumer>
<name>checkout-consumer</name>
<!--  currently supports a file path using pactFile or a URL using pactUrl -->
<pactFile>${project.basedir}/pacts/Consumer-Provider.json</pactFile>
</consumer>
</consumers>
</serviceProvider>
</serviceProviders>
<configuration>
<pact.showStacktrace>true</pact.showStacktrace>
</configuration>
</configuration>
</plugin>


Thanks a lot in advance,
Alex

PS: if you have an example it'll be awesome!
PSS: Let me know if on the right track, any feedback is welcome:)

Ronald Holshausen

unread,
Mar 8, 2015, 11:02:08 PM3/8/15
to pact-s...@googlegroups.com

Running this test generates a pact file with 3 interactions:

"interactions" : [ {
    "providerState" : "john smith books a civic",
    "description" : "book a car",
    "request" : {
      "method" : "POST",
      "path" : "/orders/",
      "headers" : {
        "Content-Type" : "application/json; charset=UTF-8"
      },
      "body" : {
        "cars" : {
          "year" : 2012,
          "model" : "Civic",
          "id" : "BBBBBBBB_ID",
          "brand" : "Honda"
        },
        "person" : {
          "firstName" : "John",
          "lastName" : "Smith",
          "id" : "AAAAAAAA_ID"
        }
      }
    },
    "response" : {
      "status" : 201,
      "headers" : {
        "Content-Type" : "application/json;charset=UTF-8"
      },
      "body" : {
        "id" : "ORDER_ID_123456"
      },
      "responseMatchingRules" : {
        "$.body.id" : {
          "regex" : "ORDER_ID_\\d+"
        }
      }
    }
  }, {
    "providerState" : "john smith books a civic",
    "description" : "retrieve data from Service-A",
    "request" : {
      "method" : "GET",
      "path" : "/persons/AAAAAAAA_ID"
    },
    "response" : {
      "status" : 200,
      "headers" : {
        "Content-Type" : "application/json;charset=UTF-8"
      },
      "body" : {
        "firstName" : "John",
        "lastName" : "Smith",
        "id" : "AAAAAAAA_ID"
      }
    }
  }, {
    "providerState" : "john smith books a civic",
    "description" : "retrieving data from Service-B",
    "request" : {
      "method" : "GET",
      "path" : "/cars/BBBBBBBB_ID"
    },
    "response" : {
      "status" : 200,
      "headers" : {
        "Content-Type" : "application/json;charset=UTF-8"
      },
      "body" : {
        "year" : 2012,
        "model" : "Civic",
        "id" : "BBBBBBBB_ID",
        "brand" : "Honda"
      }
    }
  } ]



When you execute the maven plugin in the provider project, the maven plugin will do the following:

1. Notices that the first interaction has a provider state "john smith books a civic", so will execute a POST to the state change URL configured with a body:
{
  "state": "john smith books a civic"
 }
The handler for URL should then load any appropriate data to support this state. I'd imagine that it will ensure that the John exists as a user and a Civic exists as a car.

2. Execute the POST to /orders/ and validate the response matches what is in the pact file.

3. Same as step 1 as the second interaction has the same provider state.

4. Execute the GET to /persons/AAAAAAAA_ID and validate the response matches the pact file.

5. Same again as step 1 (they all have the same provider state).

6. Execute the GET to /cars/BBBBBBBB_ID and validate the response.

Notice that the interactions are executed in the order they appear in the file (in fact they should be executable in any order).

Hope that helps. I might setup an example project, if I manage to find some time to do it.
 

kmadd...@gmail.com

unread,
Oct 2, 2015, 4:08:40 PM10/2/15
to Pact
This thread was very helpful in understanding provider states. However, I'm still a bit unclear why it is implemented the way it is.

>>>The handler for URL should then load any appropriate data to support this state. I'd imagine that it will ensure that the John exists as a user and a Civic exists as a car.

Q1) This appears to be a one time setup, so any provider state specified using "given", what is the need for it to be executed for every interaction. 

Q2) The junit or groovy dsl do not seem to allow for multiple provider states. If the state change url is executed for every interaction, why not allow for an ability to provide different payload to be posted to state change URL. This way, every interaction becomes even more customize-able and allows for more complex data setup scenarios.

Please correct me if I have misunderstood anything.

Thanks.

Bethany Skurrie

unread,
Oct 5, 2015, 12:18:25 AM10/5/15
to kmadd...@gmail.com, Pact
I think we decided to make multiple provider states available in v3 didn't we Ron?
Bethany Skurrie
Software Developer

DiUS Computing Pty. Ltd.
where ideas are engineered

Mobile: +61 415 413 713

www.dius.com.au

This email is intended solely for the use of the addressee and may contain information that is confidential or privileged. If you receive this email in error please notify the sender and delete the email immediately.

Ronald Holshausen

unread,
Oct 5, 2015, 7:24:24 PM10/5/15
to Bethany Skurrie, Krishna Maddileti, Pact

kmadd...@gmail.com

unread,
Oct 8, 2015, 9:09:59 AM10/8/15
to Pact, bsku...@dius.com.au
Hello, 

Looking through the specification link, this is precisely what I was looking for. However, this seems to be an update to the specification. I haven't found a way to incorporate this into the DSL. 

For example, in the sample code provided here

The DSL only support calling given() on the builder once. I was looking for a way to have this called everytime before .uponReceiving so that I have more control over provider state for every call.
OR
Have the provider state invoked only once for the entire test.

In terms of junit, it seems to behave like @Before but @BeforeClass method.

Ronald Holshausen

unread,
Oct 8, 2015, 6:54:19 PM10/8/15
to Krishna Maddileti, Pact, Bethany Skurrie
Hi Krishna,

That is a defect in the Java DSL. It was always meant to allow given before each request. That is how the other DSLs work, including the Groovy one in pact-jvm.

Could you raise a defect on the project to get that fixed?

Indra Nara

unread,
May 20, 2016, 9:37:56 PM5/20/16
to Pact
Hi Ron,

I tried to mock the requests that you have detailed below using the following fragment. My understanding of this is basically you POST a resource that you can later GET for contract verification purposes. Here POST is the provider state that is being set. Correct me if I am wrong in the understanding.

But, I get the following error saying that the POST has never been received -

au.com.dius.pact.consumer.PactMismatchException: Pact verification failed for the following reasons:
The following requests where not received:
Interaction: request to set the state
in state Some(some state)
request:
method: POST
path: /v1/event-publish/node-topics
query: None

Please advise on where my understanding deviated. Thanks in advance.

On Sunday, March 8, 2015 at 11:02:08 PM UTC-4, Ronald Holshausen wrote:

Indra Nara

unread,
May 22, 2016, 12:35:47 PM5/22/16
to Pact
Here is the code fragment for the above question -

return builder
.given("some state")
.uponReceiving("request to set the state")
.path("/v1/event-publish/node-topics")
.method("POST")
.body(jsonObject)
.willRespondWith()
.status(201)
.uponReceiving("request to retrieve nt")
.path("/v1/event-publish/node-topics/org")
.method("GET")
.willRespondWith()
.status(200)
.body(
new PactDslJsonBody()
.stringValue("nodeId", "org")
.stringValue("broadcastEndpoint", "google.com")
.stringValue("preference", "pubsub")
)
.toFragment();

Ronald Holshausen

unread,
May 22, 2016, 8:34:57 PM5/22/16
to Indra Nara, Pact
The mock server is indicating that it never received the POST request. You can either look at the logs (by enabling debug logging in the test) to see what actual requests are being generated. The other option is to look at the test code for this test, and make sure you are calling your implementation that does the POST.
Reply all
Reply to author
Forward
0 new messages