DEV Community

Cover image for Designing Restful APIs using an API-First Approach — Contract Test

Designing Restful APIs using an API-First Approach — Contract Test

ntakashi profile image Nicolas Takashi Originally published at nicolastakashi.Medium Updated on ・5 min read

A few days ago I wrote the second part of the "Designing Restful APIs using an API-First approach" post series, I really recommend you check the second post to understand how OpenAPI will help you have a MockServer without writing any code.
Today we will talk about contract testing and how to ensure that the API implementation meets the design proposed.

Photo by Monty Allen on Unsplash

When we talk about APIs, we are talking about contract definition, so we must have in mind that after an API is made available, the defined contract must be supported by the API provider.

Even after the API was made available, it keeps evolving, and one of the biggest challenges during an API Life-cycle is the API evolution. Because sometimes, business requirements change so much, that the current contract doesn’t meet new requirements and a breaking change is required.

😈 Breaking Changes — The root of all evil

As I usually say APIs are contracting definitions and we can’t break a contract without any bad consequence.

When a new functionality breaks the current state of the contract, if you don’t manage it very well, you will have problems with your customers that will have their clients broken after a new release of your API.

So in order to have fast feedback during the API development, you must know if a change introduces a breaking change.

🧪 Possible Approaches

There are a couple of possible approaches that can help you achieve fast feedback about breaking changes.

Unit testing

Running a unit test against your API will let you know when the contract is broken because your test will start to fail.

It's a very easy strategy to start, and almost the applications already have a unit test suit, in the order hand, this approach is too easy to be bypassed because the developer can just fix the test to succeed and move forward.

API Versioning

Create a new version of API for each new release, keeping old releases until your clients move to the new one.

We know that API Versioning is a best practice, but if you create a new version of your API, for each release, without a shadow of a doubt this will become messy very fast, and you will have big problems keeping all those versions together.

Contract Test

Check for differences between what was designed during the API Design and what was developed, running it in the Continuous Integration pipeline will provide us fast feedback, this is a very flexible and effective approach.

What approach should I use?

If we look at the first two options, we can find clear drawbacks, either by process bypass or maintainable problems.

Comparing the Contract Test against two other approaches, we can see that developers could not bypass the process without making a change in the original OpenAPI Document, this change must be reviewed for your team and if possible API Stake Holders as well.

It's easier to be automated and provide a clear understanding of what is falling and why it is failing.

🤖 Contract Test Implementation

As described above contract test seems to be the best solution to detect breaking changes in build time, and to help us during this journal, let's use a tool called openapi-diff, this is an npm package that compares two OpenApi documents and looks for what was removed or added.

OpenAPI-diff separates changes into two groups, Breaking Changes and Smooth Changes, let's see some examples:

Breaking Changes

  • Delete Path or Parameter
  • Rename a Path or Parameter
  • Add Required Parameter
  • Modify Response Item

Smooth Changes

  • Add Path or Parameter
  • Add Response Item
  • Add or Update Descriptions

To install openapi-diff is too easy, you just need to run one of the commands below.

// using npm
npm install openapi-diff

// using yarn
yarn add openapi-diff
Enter fullscreen mode Exit fullscreen mode

Test Against What?

Probably you are asking yourself, but against what we will check by differences.

A very important step before we move forward in the next steps is to extract the swagger.json generated by your code, it will depend on the platform that you are using to write your application.

In this blog post, I will use an ASP.NET Core API that will be in the same repository that is being used for this series.

To keep it simples, I won’t show how to set up the Swashbuckle in an ASP.NET Core project, but if you want to see how to do that properly, comment below and I can write a post dedicated to this setup.

ASP.NET Core and Swashbuckle CLI

Since you have Swashbuckle configured into your ASP.NET Core project, you can install a Dotnet tool named Swashbuckle.AspNetCore.Cli, it's a very simple tool that extracts the swagger.json using the project DLL, if you want to know more about this tool, please check the Github Document.

After you install the tool as described in the official documentation, you just need to run the command below.

swagger tofile --yaml --output ./bin/swagger.yaml ./api/TodoApp.Api/bin/Debug/netcoreapp3.1/TodoApp.Api.dll v1
Enter fullscreen mode Exit fullscreen mode

The command above will extract the swagger.json and convert it to a YAML file, this document looks like the OpenAPI document that we wrote during the API Design process.

OpenAPI-Diff in Action

Now that we have the OpenAPI Document that was designed and the OpenAPI Document that was generated through the code, it's time to compare those files and check the output.

Just run the command above and let see the console output.

openapi-diff ./bin/api.yaml ./bin/swagger.yaml
Enter fullscreen mode Exit fullscreen mode

Without Changes

OpenAPI Diff — No Changes Detected

With Changes

I just made some changes in the code, add a new query string parameter to the GET /tasks endpoint, and removed the status code from the GET /tasks/{id} endpoint, we can see the output below.

OpenAPI Diff — Breaking Changes Detected

As you can see above, the change in the query string parameter is not logged, but the change that removed the status code was detected as a breaking change.

Now you can just add the OpenAPI Diff to your API Pipeline to detect possible breaking changes every time that someone changes the API implementation and push it to your repository.

This strategy allows the developer to run the same script in their local machine avoiding broken pipelines, having a fail-fast validation.

🏁 Conclusion

In my opinion, avoid breaking changes is the most difficult task during the API Life Cycle, and in the majority of the time its related if the Design Session, if we take care of Design for longevity and try to understand possibles corner cases of the domain that the API aims to solve, we can reduce this necessity to break the API Contract when new business requirements appear.

In order to have breaking changes under your control, a contract test strategy may help us with fast feedback and keep us safe to break the contract and create problems for the clients of your API.

As usually every code will be updated in the Github Repository to you run to use it as a complement of this post.

Let me know what do you think about this strategy?
Do you already know it?

Comment below and let's share the experiences.
I hope you enjoy it, see you soon.


Editor guide