If you're a user of Jest, you've no doubt heard of and perhaps made use of snapshot testing.
Typically there's a direct correlation between the size and complexity of the output of a method and the length of the tests that will be written for it. Let's say you're outputting a class that contains 20 properties. Congratulations! You get to write 20 assertions in one form or another for each test case. Or a single assertion whereby you supply the expected output by hand specifying each of the 20 properties. Either way, that's not going to be fun. And just imagine the time it would take to update multiple test cases if you wanted to change the behaviour of the method in question. Ouchy.
Time is money kid. What you need is snapshot testing. Say goodbye to handcrafted assertions and hello to JSON serialised output checked into source control. Let's unpack that a little bit. The usefulness of snapshot testing that I want in C# is predominantly about removing the need to write and maintain multiple assertions. Instead you write tests that compare the output of a call to your method with JSON serialised output you've generated on a previous occasion.
This approach takes less time to write, less time to maintain and the solid readability of JSON makes it more likely you'll pick up on bugs. It's so much easier to scan JSON than it is a list of assertions.
shouldMatchSnapshot. However getting to nearly the same place in C# is delightfully easy. What are we going to need?
First up, a serializer which can take your big bad data structures and render them as JSON. Also we'll use it to rehydrate our data structure into an object ready for comparison. We're going to use Json.NET.
Next up we need a way to compare our outputs with our rehydrated snapshots - we need a C#
shouldMatchSnapshot. There's many choices out there, but for my money Fluent Assertions is king of the hill.
Finally we're going to need Snapshot, a little helper utility I put together:
Let's look at the methods:
Load. Make is what we're going to use to create our snapshots. Load is what we're going to use to, uh, load our snapshots.
What does usage look like? Great question. Let's go through the process of writing a C# snapshot test.
First of all, we're going to need a method to test that outputs a data structure which is more than just a scalar value. Let's use this:
Yes - our trusty
LeopardService. As you can see, the
GetTheLeopards method returns an array of
Leopards. For now, let's write a test using
Snapshot: (ours is an XUnit test; but
Snapshot is agnostic of this)
Before we run this for the first time we need to setup our testing project to be ready for snapshots. First of all we add a
Snapshot folder to the test project. The we also add the following to the
This includes the snapshots in the compile output for when tests are being run.
Now let's run the test. It will generate a
With our snapshot in place, we comment out the
Snapshot.Make... line and we have a passing test. Let's commit our code, push and go about our business.
Someone decides that the implementation of
GetTheLeopards needs to change. Defying expectations it seems that Dotty the leopard should now have 90 spots. I know... Business requirements, right?
If we make that change we'd ideally expect our trusty test to fail. Let's see what happens:
Boom! We are protected!
Since this is a change we're completely happy with we want to update our
leopardsSnapshot.json file. We could make our test pass by manually updating the JSON. That'd be fine. But why work when you don't have to? Let's uncomment our
Snapshot.Make... line and run the test the once.
That's right, we have an updated snapshot! Minimal effort.
This is a basic approach to getting the goodness of snapshot testing in C#. It could be refined further. To my mind the uncommenting / commenting of code is not the most elegant way to approach this and so there's some work that could be done around this area.