accept or act in accordance with a rule, decision, or recommendation.
At Beme the majority of our APIs and services are written in Go. For us, a critical part of deploying changes to a production API daily is ensuring our mobile apps receive no unexpected responses. This raised two questions about our testing.
- How can we ensure our tests capture all parts of an http response?
- When we update any component of our API, how can we visualize the changes that are made?
We could solve this using the existing testing and httptest packages by checking each component of the response. But this is exhaustive and prone to error. The developer would have to keep track of any changes made and ensure those reflect in each and every test appropriately.
We wanted a better way.
What we ultimately came to was snapshotting http responses, comparing between test runs, and alerting the developer of any changes. This results in a lockfile representing the agreed upon state of our API at that time. It ensures that our API abides (😉) by a set of rules.
So… what’s a snapshot?
In the case of an http response, a snapshot is the formatted http dump (header & body) of an http response, identified by a unique key.
Consider this http handler function
Using abide, we obtain the resulting snapshot
This is a fairly simple example, but imagine a more complex scenario in which the handler sits behind a number of middleware, the response body is more complex, etc. The difficulty of testing increases dramatically. This is where
abide shines brightly.
How do we use it?
abide in nearly all of our http endpoint tests to help avoid writing verbose, and possible fragile, tests.
abide executes during the standard test run,
$ go test and outputs in the test results. If the output does not match the existing snapshot, the diff will be outputted in test results. If it is an intentional change, the snapshot can be updated via
$ go test -- -u.
We’ve included an example in the project, see here.
Note: a generic
Assert method has be included so that any object that implements
String() string can be tested via abide.
How does it help us?
First, it works extremely well with version control. We are able to get a visual diff of how our API endpoints change over time just by running our tests. And for the reviewer(s) of a PR it can help decipher what exactly is changing.
Secondly, it quickly improves test coverage and acts as a catch all to our regular http testing. We can focus more on how our APIs influence state (database, cache), and less on boilerplate code for checking status codes, etc.
Thirdly, it ensures that changes to one part of our system are caught across all endpoints. This is particularly helpful (and time saving) when making improvements to middleware, helper functions, etc.
So what now?
Give abide a swing and let us know what you think. Issues, contributions, etc. are all more than welcome! 😃