Pact: how to set up provider states

3.6k views Asked by At

I'm looking at the Python implementation of Pact and trying to set up provider states. It seems to say that the way to do it is for the provider to have an endpoint built into the service that is called to put the provider in the correct state. The issue is I can't find any documentation on what that endpoint is actually supposed to look like. What is the input, what does it return, etc.

I tried looking at the default ruby implementation, and it seems to imply a completely different mechanism for putting the provider into a certain state. This looks like it uses a ruby module that gets required by the verifier script, with no HTTP requests involved at all.

What is the correct way to set up a provider state? If it requires setting up additional endpoints, I need to know what that endpoint is supposed to look like. If it requires a class/module to be imported to the verifier script, I need to know how that is implemented in languages other than ruby.

2

There are 2 answers

6
J_A_X On BEST ANSWER

As per the documentation in Pact-Python, it's a bit open ended how you actually accomplish this. Personally, how I would do it for say, a node provider as I normally don't work with Python, is within my provider tests, I would create a server on an unused port whose purpose is to receive states from pact and set them up properly. Once you run the tests, this small server would get hit from pact with a JSON file that includes, the consumer, provider and the state.

For example, here's a node example:

var http = require('http');

beforeAll(function(){
    // PROVIDER STATE LISTENER 
    http.createServer(function (req, res) {
        var body = [];
        // Ignore this bit, this is just how node does server request/response
        req.on('data', (chunk) => {
            body.push(chunk);
        }).on('end', () => {
            // Get body, parse JSON.  JSON includes 'consumer' string and 'states' array of string
            var json = JSON.parse(Buffer.concat(body).toString());


            // THIS IS WHERE YOU NEED TO SETUP YOUR STATE
            res.status = 200; 
            switch(json.state) {
                case "When User does something": // this is the actual name of the state that's specified by your consumer, which is found in the contract
                    // Setup any data that relates to your state here, like adding rows to a DB, setting environment variables, etc
                    break;
                // Add another states that are used in your provider tests
                default:
                    res.status = 500;
                    res.statusMessage = "Missing state '" + json.state + "'";
            }
            res.end(); // Send the response back
        });
    }).listen(9001);
})

// Run your tests
it("Test Pact Interactions", function() {
    return pact.verifyPacts({
        // options here
        providerStatesSetupUrl: "http://localhost:9001"
    });
});

I hope that makes sense.

0
Beth Skurrie On

Here is the documentation for provider states https://github.com/pact-foundation/pact-provider-verifier/#api-with-provider-states

API with Provider States

Read the Provider States section on docs.pact.io for an introduction to provider states.

To allow the correct data to be set up before each interaction is replayed, you will need to create an HTTP endpoint (which may or may not actually be in the same application as your provider) that accepts a JSON document that looks like:

{
  "consumer": "CONSUMER_NAME",
  "state": "PROVIDER_STATE"
}

The endpoint should set up the given provider state for the given consumer synchronously, and return an error if the provider state is not recognised. Namespacing your provider states within each consumer will avoid clashes if more than one consumer defines the same provider state with different data.

The following flag is required when running the CLI:

--provider-states-setup-url - the full url of the endpoint which sets the active consumer and provider state.

Rather than tearing down the specific test data created after each interaction, you should clear all the existing data at the start of each set up call. This is a more reliable method of ensuring that your test data does not leak from one test to another.


Note that the HTTP endpoint does not have to actually be within your application. It just has to have access to the same data store as your app. So if you cannot add "test only" endpoints during your test suite, consider making a separate app which shares credentials to your app's datastore.