This is the third instalment of a three part series on schema testing. In part one of the series, we discussed the differences between schema testing, specifications and contract testing, and highlighted the trade-offs of each. In part two, we reviewed the most popular approaches to schema testing. In this final post, we demonstrate how you can use Pactflow to manage a schema-based contract testing workflow and talk about how we might be able to incorporate it approaches into our tooling in the future to support a wider range of testing.
Schema-based contract testing with Pactflow
So now that we have a grasp on the key concepts and trade-offs, let's see how we can integrate Pactflow with the code-generation approach using JSON Schema as the request/response body validator - note that as long as you can capture the contract in a file, any of the above approaches are also compatible.
I would class this as a consumer-driven, schema-based contract testing methodology, as it allows validating a contract in the provider using the same workflows you would with a standard Pact test.
This phase is analogous to the Pact consumer unit testing phase, where the consumers' client code is tested and as an output produces a contract.
In our example, we are generating a JSON schema from our statically typed language (TypeScript) using the typescript-json-schema library. The process:
- Author the API consumer type (e.g.
- Generate the contract (using the TypeScript
- Publish the contract to Pactflow and run the
- If successful, deploy to production and tag the application version as moved to
This phase is analogous to the provider verification step. It uses json-schema-diff to do a semantic comparison of contract submitted by the consumer against the JSON schema to check for backwards incompatible contract changes.4. Fetch the consumer contract
- Download the schema from Pactflow
- Generate a contract from the provider's code base
- Perform the diff using
- Also compare against the current "golden" (published) schema (this is particularly useful for public APIs and ensure documentation doesn't drift) Download the schema from Pactflow
- Send the verification results (pass/fail) back to Pactflow
- Run the
- If successful, "deploy" to production and tag the application version as moved to
Using Pactflow this way resolves the evolution (8) and sharing (9) problems, whilst preserving many of the powerful workflow features evolved through the Pact ecosystem over the past 7 years.
Swagger/OpenAPI Specification and Pactflow
As alluded to earlier, it is actually possible to combine the above strategies. In fact, Atlassian have created several useful tools to allow teams to pick and choose what works best for them. One example is their Swagger Mock Validator, which allows teams to write consumer Pact tests - getting all of usual benefits Pact provides - whilst allowing Providers who test against a Swagger/OAS spec to check whether or not they are compatible with consumers.
It's the usual Pact story: the consumer authors a series of unit tests against their API client and publishes the resultant contract to the broker.
The process is similar to above, where the provider verification step simply performs a static schema comparison between the Pact file and the OAS specification held by the provider. If the pact is a subset of the OAS spec, the test will pass.
This alleviates several of the challenges mentioned above and really helps to decouple teams, whilst providing many of the guarantees. It does hinge on the ability to ensure your API does implement the OAS.
I've used this strategy in the past, as well as simply added it as an overlay to Pact tests to ensure any published swagger file is indeed consistent with the implementation.
You can check out the project here: https://bitbucket.org/atlassian/swagger-mock-validator/
All of the options have their tradeoffs, and you need to choose what makes sense for you. The "Pact workflow" can be more involved, but the schema-based tests provide weaker guarantees (and perhaps also will suffer at a certain scale anyway).
Regardless of the approach, however, there is always a need for some central repository to store and facilitate the evolution of the contract - in whichever form that takes. Pactflow is ideally suited to play this role.
I think an exciting possibility is to combine these two approaches: enhancing the code-based contract tests with schemas. By introducing tools for evolution (Pactflow) and capturing the supported conversations (specification by example) we can solve most of the issues described above, reduce duplication and coupling between teams, whilst preserving the benefits of the Pact approach. It also introduces the possibility of a more seamless specification-first approach to API design. It also allows for both: consumer or provider initiated 🤯.
This makes it a "choose your own adventure" to the testing approach, whilst enabling a consistent continuous delivery workflow.
In fact, in the near future we will be looking into supporting a Provider-driven approach to contract testing (see also this RFC), which could simplify incorporating these ideas and allow even more flexibility in the approach.
Perhaps this is like the attempts to reconcile general and special relativity, and will result in a more complicated mess for everyone, but this author thinks it may just be the next major evolution in contract-testing (pun intended).