Ensure public API surface using Approval-Tests

One of the things that made me think for some time is “How can I ensure my public REST-API does not change by accident?”.
Why
With todays IDE’s we have very powerfull automatic refactorings which is cool. The downside is that we easily can refactor a large pile of code what can affect the public API’s without the developer notice it.
When my public API’s (for example a REST-API’s my backend offers to partner companies) changes without notice it will be found very late the software development lifecycle. Worst case: after the release. This will result in a lot of work (and cost) for everybody involved.
The idea
So “Where is the problem?” you may ask: “Just write a bunch of unit-tests!”. The problem with this is that with the powerfull refactorings we usualy also refactor the unit-test code in one go. So the unit-test never becomes red even if we changed the public API. Therefore plain unit-tests are useless for this kind of tests.
We need something that does not get refactored automatically when we use automatic refactorings.
What I came up with is using ApprovalTests to dump the relevant data a in a approved-file (aka expectation file). This becomes our exectation of the unit-test result. The approval files don’t get changed by refactorings.
When they change I as a developer have to approve the new content manually (with the help of my diff tool) and therefore get noticed by my unit-test suite that something in the API changed.
How
ApprovalTests implementations are availlable for all major platoforms. For .Net you can install this Nuget package.
To test may REST-API’s I basically call action methods of my WebAPI controllers, serialize the output as Json and verify it using Approvals.
[UseReporter(typeof(DiffReporter))] public class PersonControllerTests { private readonly IPersonRepository personRepo = A.Fake<IPersonRepository>(); private readonly PersonController controller; public PersonControllerTests() { controller = new PersonController(personRepo); } [Fact] public async Task GetDetails_Success_MustReturnObject() { // Arrange var personId = new Guid("51bd89eb-5ae6-4cfe-954a-f3f6a51352ab"); // Act var result = await controller.GetDetails(personId) // Assert Approvals.VerifyJson(JsonConvert.SerializeObject(result)); } }
To enhace this one can also store the verb’s and URL’s. To do so you can wrap the above result
in an anonymous object where you store additional information and serialize all of that into Json and approve it. It would be recommeded to implement a helper for doing this.
Alternative idea:
For REST-API’s an alternative idea is to generete the Swagger.json file you normaly need anyway and store it in your repo. Write a unit-tests that compares a freshly generated Swagger.json to the existing one. This would need some more advanced testing code / helpers but result in verify every aspect of the public API (Url’s, HTTP-Verbs, data-structures, supported headers, query-parameters, etc.)
Categories
Cool! It is indeed a necessity when you develop the API for the external customers/consumers. If you are doing it for the internal purposes, you could also write Consumer Driven Contracts. That way you can ensure that either you didn’t break any consumers, or if you still don’t have any consumers, you can freely change the API to your liking.
LikeLike