My most recent API establishes a data model for checklist templates, which Dr. Atul Gawande made famous in his 2009 work, The Checklist Manifesto (which, in all honesty, I’ve never actually read). The idea is simple: successful execution of an important procedure relies on well-crafted checklists, faithfully reviewed and executed. The Checklist API makes this idea available programmatically, by allowing consumers to define checklist templates, create instances of those templates, and finally tick off items from each individual checklist. The source code is available at https://github.com/shaisachs/checklists.

The details of the data model are about what you’d expect from a templating system. Consumers create checklist templates using POST /checklistTemplates, add an item to a template using POST /checklistTemplates/{id}/items, and finally create checklists based on those templates using POST /checklists, with a checklistTemplateId specified in the body. Items on a checklist may be marked complete with PUT /checklists/{checklistId}/items/{itemId}.

While that data model is all to the good, what’s happening underneath the hood is, in my view, far more exciting. There have been two major developments in my API code base in recent months. First, I’ve added behavioral tests to verify the behavior of the routes. Second, I’ve abstracted about as much code as possible into a separate framework class project.

Behavioral testing is an approach to testing which is something like functional, or integration, tests - except that behavioral tests are not too concerned with the external dependencies of a body of code. I’ve always been a big fan of writing integration tests for API, because these types of tests are easy to write, and even better they are easy for others to read. Moreover, an API integration test can quickly cover a large swath of underlying code, thereby providing great bang for the buck in terms of available testing resources.

My thinking on developing behavioral tests in the context of an ASP.NET Core API was largely shaped by Arnaud Auroux’s blog post on the subject, and I happily incorporated his IStartupConfigurationService interface, with a few tweaks, into this code base. The gist of this idea is to let the testing code fine-tune its startup configuration via an injectable service, so that dependencies like the database can be abstracted away in readily testable format. Auroux’s implementation injects SQLite as a database dependency, but in my case I was able to get away with the Entity Framework in-memory provider, which was even easier to configure. I also borrowed some tips from Dave Paquette’s post on database setup to ensure that the database was properly populated with test data. Probably the main difficulty was injecting proper test configuration values via an appsettings.Test.json file - it took a little digging to figure out that I needed to explicitly include the file in the MS Build output via the csproj file.

The other major development in this code base is the migration of most of the functionality into a new Framework class library. Relatively speaking, this work was not too difficult - it was mainly a question of moving base classes around and carefully tweaking namespaces here and there. Needless to say, the test code was an exceptionally valuable asset as I proceeded with the migration. Once I got everything to compile, it was a trivial matter to ensure that the functionality was actually working as desired. And of course, I didn’t stop with moving the basic functionality of the API into the framework project - I also moved almost all of the test code into the framework. As a result, it’s extremely easy to roll out new routes which are not only perfectly functional, but behaviorally tested into the bargain.

Just to show you I mean business with all this testing mumbo-jumbo, I haven’t actually published the code in Azure or the RapidAPI Marketplace, as I’ve done with previous APIs. (And, ok, I was also too lazy to bother with all of that rigamarole.) The testing code gives me a great deal of confidence that the code is working - even without a production implementation to kick around!

I’m really excited by how nicely this API came together. Naturally, it’s tempting to abstract things further, by moving the Framework project into its own code base, and allowing it to graduate into its own nuget package, or something of the sort. I’ll almost certainly dive into that process in the near future.