Pact JS: Provider testing

984 Views Asked by At

Using Pact JS to test both my consumers and providers. I’m successfully able to generate a pact file, and I’d like to verify those against my provider.

I have a few questions with regards to testing the provider:

  1. Do I need to start my provider service before I can do the tests? Should I be hitting the actual provider endpoint in the test? For example, say I have a GET /dogs endpoint on my provider code base. When I run my tests, do I need to start up my service locally, hit the /dogs endpoint and then verify the response returned for the endpoint with the pact file?
  2. If I want to run this as a part of my CI pipeline (I’m using CircleCI), what are some of the best practices to follow? Do I need to start up my service from the circleci build step, point to some database somewhere and then follow the steps above?
  3. Are there any concepts of using stubs on provider testing? If so, how do these work for staring a provider service. Is there any examples or sample code for this?
1

There are 1 best solutions below

0
On

Do I need to start my provider service before I can do the tests? Should I be hitting the actual provider endpoint in the test?

Yes, you start up your provider, and use the actual endpoint (see below for a caveat)

When I run my tests, do I need to start up my service locally, hit the /dogs endpoint and then verify the response returned for the endpoint with the pact file?

Sort of. Pact's mock consumer will do the hitting of the endpoint and comparing the results with the expected response in the pact file for you.

If I want to run this as a part of my CI pipeline (I’m using CircleCI), what are some of the best practices to follow?

This is kind of a general question, but you would usually create a build step that starts the service, runs Pact's verification, and then tears down the service. See the end of the post for a link to an example.

Are there any concepts of using stubs on provider testing? If so, how do these work for staring a provider service?

So, pact is a contract testing tool. Contract testing has some important differences from functional testing.

Broadly, contract testing is about verifying that the shape of the data that you're sending and receiving is agreed upon and can be understood by both parties. It's not about verifying that the data behaves correctly. Say I have the following design for an API that takes a string representing a phone number:

  • It's valid to add local phone numbers
  • It's valid to add phone numbers with a national dialing code
  • It's valid to add phone numbers with a country dialing code
    • It can start with a + or 00
  • It's invalid to add a phone number with a + anywhere other than the start
  • It's invalid to add phone numbers under a certain length

Even though there are a lot of cases there, it's appropriate for Pact to only have a success and a fail case:

  • Tried to add a valid phone number -> whatever the success response is
  • Tried to add an invalid phone number -> whatever the invalid phone number response is.

(unless the API returns different responses for different types of fails, of course).

The contract is about what can be expressed, rather than why it is expressed.

This tells us where we can use stubs - your API endpoint workflow might look something like this, in pseudo code:

// end point for "add phone number"
request = unmarshalResponse(data)
success = recordPhoneNumber(request)
response = marshallResponse(success)
send(response)

Because we're only interested in the contract, it's appropriate to stub out the business logic in recordPhoneNumber(), meaning that pact will test your network layer and your marshallers.

Depending on your code design, the best place to put that stub will change. In our example, the stub could look something like this:

// stub for recordPhoneNumber()
if (request.phoneNumber === "+12 345 1234 123") {
   return new Valid()
} else if (request.phoneNumber === "123") {
   return new Invalid("Number too short")
}

Ideally, contract testing won't need any of your infrastructure beyond the endpoint (databases, caches, etc). So, ideally, you can put your stub in a place where you won't need to fire up those things.

Pact is not a good fit for an integration testing tool (although it is possible to use it that way, you'll have other problems if you do).

  • But how does my provider know which stub to apply?

You can achieve this with provider states. Here's a real example from the javascript provider example:

server.post('/setup', (req, res) => {
  const state = req.body.state

  animalRepository.clear()
  switch (state) {
    case 'Has no animals':
    // do nothing
    break
   default:
    importData()
  }
}

Is there any examples or sample code for this?

Yes. Check out this javascript provider example