Introducing a library for OpenAPI contract testing with Go's net/http package

Featured image for sharing metadata for article

I've recently been working on building a Go API, which is driven through an OpenAPI specification, and has almost all of the code generated through the awesome oapi-codegen.

One of the common concerns of using OpenAPI, or really any documentation, that is always difficult is keeping the documentation up-to-date with the implementation, which is hard regardless of how big your project is, and how much of the code you can generate from your documentation, or vice versa.

Even worse is spending a lot of engineering time on a feature, shipping it, and then finding out that you've broken your contract with the consumer, and you need to think about rolling back, or fixing forward.

Contract testing is the solution to this, and allows us to think about it much earlier in the life cycle, giving service owners the chance to discover issues much sooner, and fix it before it causes outages.

There's an excellent post on the APIs You Won't Hate blog in a similar vein for a Laravel PHP application, which inspired me to think about this as a problem I'd like solved for Go.

It's common to use the net/http handlers in Go code, so I thought that'd be a good place to start with solving this. Before barrelling into solving it myself, I looked into options and found kin-openapi has a filter for validating request/response, but I couldn't see how best to make this work with test code and give me errors in my tests.

Asking around on the Go community Slack, and APIs You Won't Hate community Slack, I was recommended this great solution from Bojanz Zivanovic, which uses kin-openapi's filter, and wraps it in something that can be used in tests - awesome!

To make it a little more reusable, I've wrapped it into a library, which can be found on GitLab, and allows you to write the following code:

import (
	validator "openapi.tanna.dev/go/validator/openapi3"
)

func Test_Handler_Contract(t *testing.T) {
	doc, err := openapi3.NewLoader().LoadFromFile("testdata/fedmodel.yaml")
	if err != nil {
		t.Error("unexpected test setup error reading OpenAPI contract", err)
	}

	v := validator.NewValidator(doc, t)
	rr := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/apis", nil)
	req.Header.Add("tracing-id", "123")

	Handler(rr, req)

	v.Validate(rr, req)
}

This then fails your test if you don't have a correct request contract, as well as checking that the response is well formed, based on properties defined in your OpenAPI.

I've already found it's been good to validate some existing OpenAPI documents + their implementations, and I hope to hear of others using this!

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#go #openapi #testing #contract-testing.

Also on:

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.