{
"$type": "site.standard.document",
"canonicalUrl": "https://johnnyreilly.com/posts/snapshot-testing-for-c",
"description": "Snapshot testing is an efficient test technique for comparing outputs with JSON. Its applicable to C# too, using Fluent Assertions and a helper tool.",
"path": "/posts/snapshot-testing-for-c",
"publishedAt": "2018-11-17T00:00:00.000Z",
"site": "at://did:plc:yy3apqjlms24kso7ahn7lbmb/site.standard.publication/3mova7c4nho2b",
"tags": [
"c#",
"automated testing"
],
"textContent": "If you're a user of Jest, you've no doubt heard of and perhaps made use of snapshot testing.\n\n\n\nSnapshot testing is an awesome tool that is generally discussed in the context of JavaScript React UI testing. But snapshot testing has a wider application than that. Essentially it is profoundly useful where you have functions which produce a complex structured output. It could be a React UI, it could be a list of FX prices. The type of data is immaterial; it's the amount of it that's key.\n\nTypically 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.\n\nTime 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 Cis 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.\n\nThis 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.\n\nPutting the Snapshot into C\nNow if you're writing tests in JavaScript or TypeScript then Jest already has your back with CLI snapshot generation and shouldMatchSnapshot. However getting to nearly the same place in Cis delightfully easy. What are we going to need?\n\nFirst 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.\n\nNext up we need a way to compare our outputs with our rehydrated snapshots - we need a CshouldMatchSnapshot. There's many choices out there, but for my money Fluent Assertions is king of the hill.\n\nFinally we're going to need Snapshot, a little helper utility I put together:\n\nLet's look at the methods: Make and 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.\n\nWhat does usage look like? Great question. Let's go through the process of writing a Csnapshot test.\n\nTaking Snapshot for a Spin\n\nFirst 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:\n\nYes - 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)\n\nBefore 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 .csproj:\n\nThis includes the snapshots in the compile output for when tests are being run.\n\nNow let's run the test. It will generate a leopardsSnapshot.json file:\n\nWith 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.\n\nTime Passes...\n\nSomeone 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?\n\nIf we make that change we'd ideally expect our trusty test to fail. Let's see what happens:\n\nBoom! We are protected!\n\nSince 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.\n\nThat's right, we have an updated snapshot! Minimal effort.\n\nNext Steps\n\nThis 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.\n\nHappy snapshotting!",
"title": "Snapshot Testing for C#"
}