Contract Testing in .NET Core
Consumer driven testing setup for .NET Core based API's using Pact-net
Why even bother with Contract Testing?
Disclaimer: this post is self-standing but is still part of the on-going series about testing practices in .NET Core & CA, I’d urge you to consider reading the whole series ;-)
Let’s have a quick review of the application that we are writing our test suite for:
The setup is straightforward:
The UI client interacts with the our API
Our API in turn interacts with a database & an 3rd party API via HTTP.
In part V we covered how one can use Wiremock to deal with the 3rd party HTTP API dependency when we run our test suite however…
This setup has a flaw — we do not catch any breaking changes in the external API’s contract. Here is where contract testing comes into play.
What is contract testing?
Contract testing is a technique that is often used in Microservices systems as a way to ensure that a new version of a Microservice does not break it’s downstream dependencies.
The idea behind contract testing is really simple:
The consumers define & store a set of contracts (called pacts) that they expect the provider to fulfil.
When the provider wants to release the a new version it retrieves the available pacts it needs to fulfil and runs a set of tests to check that.
In case the pacts are fulfilled a new version of the provider is release, otherwise more work is needed in order to offer backwards compatibility.
As part of the PoC for an ongoing project I took a look into Pact.NET which is a popular choice for contract testing in .NET, so let’s take a look at how we can use it.
Setting up consumer contracts
PS: I’m running the CardGameStore.API and Notifications.API as part of a single solution for simplicity sake. In a real world scenario these would be 2 different solutions, just saying…
To get started we will need to:
Create a new xUnit test project in our solution which we will call CardGameStore.Tests.Contract
Install 2 NuGet Packages:
— PactNet— PactNet.Output.Xunit
Create a small test that we will use to generate a Pact for our provider:
Once we run this xUnit test we will end up generating a json based file which needs to be shared with the Provider API during our CI process.
If you are curios about setting up a CI/CD pipeline with C# and Nuke here is a tutorial you can follow.
Setting up provider tests — a.k.a. The tricky part….
— Setting up the provider tests is somewhat trickier in nature. There are a couple of issues here:
The first issue:
“If your tests are using TestServer or WebApplicationFactory then these are running the API with a special in-memory test server instead of running on a real TCP socket. This means the Rust internals can't call the API and therefore all of your provider tests will fail. You must host the API on a proper TCP socket, so that they can be called from non-.Net code.” — official read.me
The second issue:
An API might have dependencies like databases, queues etc. that you might want to set-up or even better mock out, since this is not integration testing and what we want to achieve is to validate the contracts.
Let’s take a look at the Notifications.API
— Since I need to keep this post short I’ll go over the main points of interest only.
A constraint I was looking to meet (imposed mostly by curiosity and the current work place project) I had to use/adjust the setup to the old-fashioned Startup setup we had in dotnetcore for years with the use of .NET 8 and using Minimal API’s… To do that I had to:
Defined an abstract BaseStartup class.
Created an extension method for the WebApplicationBuilder
Created a Startup.cs where all the configuration will take place. (With an additional IHelloService, which will be a small sample on how to Mock specific dependencies if you need to)
Defined a couple of minimal API endpoints that will fit the pact we wrote previously using the approach which I covered in my MinimalAPI course on YouTube.
And wired it all up in the Program.cs
With all that in place let’s move onto the Contract Testing setup
The steps here are:
Like previously we will have to create an xUnit test project which I will call Notifications.Tests.Contract and install the same NuGet packages mentioned in the consumer part.
Since we might need to mock out specific services I created a TestStartup class to set up overrides
Set up a Test that will run an instance of the Notifications.API and verify if it satisfies the required pacts
And finally running this test would result in a similar output to this one:
which frankly speaking is really easy to debug if things go wrong…. and yes they did first time around :)
An important note here is this one:
“We are tracking events anonymously to gather important usage statistics like Pact version and operating system. To disable tracking, set the 'PACT_DO_NOT_TRACK' environment variable to 'true'.” — PactNet
Summing it all up
Contract testing is another tool you can add to your toolset. And as always you need to consider where to use it.
This one is personal but e2e tests are the bane of my existence when it comes to microservices… so I would look for places where I cloud get rid of the clunky e2e tests in favor of contract based testing between microservices.
That said contract testing wouldn’t be efficient in cases like:
Modular monoliths — as long as the modules are part of the same assembly, in case those are decomposed you can start looking into contract testing.
Dealing with well established API’s / Services — those more often than not always take care of being backwards compatible and offer time/warnings for migration in case it is needed.
Next steps
If you liked this kind of content/tutorial I will be really grateful if you cloud share it among your colleagues and friends in order to grow the newsletter further.
Heads up:
I release an entire course around the topic of building a Modular Monolith system that you can find clicking here : Microservices Ready Architecture
Become a Patreon and get access to the source code presented in the newsletters and YouTube channel.
And with that cya next time :-)