Lars-Erik's bloghttp://blog.aabech.no/blog/
Ramblings about Umbraco, .net and JavaScript development. With a sprinkle of other stuff.Articulate, blogging built on Umbraco1133http://blog.aabech.no/archive/exploiting-approvaltests-for-clearer-tests/
unit testingExploiting ApprovalTests for clearer tests<h2>What's this for?</h2>
<p>Ever written several asserts in one test because you have a big graph you want to verify? How 'bout files or maybe <a href="/archive/testing-views-with-razorgenerator">razor views</a>? Several asserts often clutter up the tests. Big strings also make the actuall calls hard to see for all that content. Putting big strings in files is a good idea to avoid that, but few people do. It adds another &quot;menial&quot; task when you're in the zone. But what if it was dead easy?</p>
<p>I once maintained a Resharper extension, and their example tests had so called &quot;gold&quot; files that they compared to output. The squigglies was represented by special characters around terms. So they just compared the output of a text renderer to the text in a file. Great idea. One limitation though: with a regular Assert.Equals you just see the segment around the first mismatch. Guess what, there's a tool that's been around for at least 10 years that solves all those issues, and more.</p>
<h2>Approval tests, eh?</h2>
<p>Sounds a bit like acceptance tests, right? Don't be fooled, it's purpose is to serve all the way down to the &quot;unit test layer&quot; of your tests. I've found it to be a huge timesaver, as well as making my tests so much more clear.</p>
<p>I know you're thinking &quot;Shut up and give me an example, then!&quot;, so let's have a look. I've got this unit test from my post about <a href="/archive/testing-views-with-razorgenerator">testing razor views</a> that I never really asserted anything in. I just output the result to console and assert inconclusive. Here it is for reference:</p>
<pre><code>[TestFixture]
public class When_Displaying_An_Event
{
[Test]
public void It_Is_Rendered_With_A_Name_Date_And_A_Link()
{
var view = new _Views_Partials_Event_cshtml();
var actionResult = GetConcertEvent();
Assert.AreEqual(&quot;Event&quot;, actionResult.ViewName);
var renderedResult = view.Render((Event) actionResult.Model);
Console.WriteLine(renderedResult);
Assert.Inconclusive(&quot;Way too big to assert here.&quot;);
}
}
</code></pre>
<p>It outputs the following HTML:</p>
<pre><code>&lt;div&gt;
&lt;a href=&quot;https://eventsite/123&quot;&gt;
&lt;label&gt;
Concert of your life
&lt;/label&gt;
&lt;span&gt;
fredag 31. desember 2049 23.59
&lt;/span&gt;
&lt;/a&gt;
&lt;/div&gt;
</code></pre>
<h2>Let's see it</h2>
<p>Now let's assert this with ApprovalTests. To use it, you just <code>install-package ApprovalTests</code> with your trusty package manager console. Make sure to install it in your test project. ;)</p>
<p>Now instead of <code>Assert</code>, we ask ApprovalTests to <code>Verify</code> our data instead. It even has a special overload for this concrete case: <code>Approvals.VerifyHtml</code>. So we rewrite the test as such:</p>
<pre><code>[Test]
public void It_Is_Rendered_With_A_Name_Date_And_A_Link()
{
var view = new _Views_Partials_Event_cshtml();
var actionResult = GetConcertEvent();
Assert.AreEqual(&quot;Event&quot;, actionResult.ViewName);
var renderedResult = view.Render((Event) actionResult.Model);
Approvals.VerifyHtml(renderedResult);
}
</code></pre>
<p>Now when we run our test, we get this nice little welcoming message from ApprovalTests:</p>
<p><img src="http://blog.aabech.no/blog/media/1033/welcome-to-approvaltests.png" alt="Exception: Welcome to ApprovalTests" /></p>
<p>It tells us we're missing a vital part: A reporter. It's possible to use <code>DiffReporter</code> to launch your favorite configured difftool. But if you're in Visual Studio, there's a special treat: <code>VisualStudioReporter</code>. Let's add that to our fixture and see what happens:</p>
<pre><code>[UseReporter(typeof(VisualStudioReporter))]
[TestFixture]
public class When_Displaying_An_Event
{
[Test]
public void It_Is_Rendered_With_A_Name_Date_And_A_Link()
{
// ...
}
}
</code></pre>
<p>And we run it:</p>
<p><img src="http://blog.aabech.no/blog/media/1032/first-result.png" alt="ApprovalTests first result have empty approved" /></p>
<p>What hits you is probably the big failure statement there, but look again - up at the top there.
We've got a diff opened, the result on the left hand and a big green field on the right side.<br />
What just happened is that ApprovalTests took our result, stored it in a received file, and at the same time wrote an empty &quot;approved&quot; file. It then proceeded to compare, and finally pop a diff of those two files.<br />
Isn't that just beautiful? Everything that makes your test fail in one clear diagram.</p>
<p>The &quot;procedure to follow&quot; here, is to &quot;approve&quot; results when you're happy. To do that, you just copy and paste. Let's do that now:</p>
<p><img src="http://blog.aabech.no/blog/media/1030/first-approved.png" alt="First approved with invalid indenting" /></p>
<p><em>If you've got ReSharper, it's probably gonna try to format everything nicely when you paste. To have it in the original, ugly indentation state, press Ctrl+Z (undo) once after pasting.</em></p>
<p><em>Regarding the indentation that's off. It seems to be a bug with HTML in the current version of ApprovalTests, so stupid me for choosing this example. I'll update the post if it gets fixed.</em></p>
<p>We can now run our test again, and it's gonna pass. When it passes, it doesn't bother to open the diff.</p>
<p><img src="http://blog.aabech.no/blog/media/1031/first-passing.png" alt="It passes with an approved file" /></p>
<p>This means we're just gonna get diffs for whatever is currently failing. Even if we run our entire suite of tests. Now there's a couple of housekeeping things to keep in mind:</p>
<h2>Commit approvals only</h2>
<p>If you were paying attention, you noticed we got two files adjacent to our test source file. One is named [TestFixtureName].[TestMethodName].received.html and one is named [TestFixtureName].[TestMethodName].approved.html. If you ran a test that passed, you'll actually just have your approved file. <strong>You want those approved files in source control!</strong></p>
<p>The received files though, might end up not being cleaned up for one or the other reason. Hopefully just that you didn't bother to commit a fully passing build. I'm sure you didn't do that to the master branch, though. In any case, make sure to <em>ignore</em> your received files. This pattern in .gitignore generally does the trick:</p>
<p><code>*.received.*</code> </p>
<h2>That's just the tip of the iceberg</h2>
<p>We've seen the <code>VerifyHtml</code> bit. One of my personal favorites is it's sibling <code>VerifyJson</code>. I keep an extension on <code>object</code> in my tests, called <code>.ToJson()</code>. With it, I can just go:</p>
<pre><code>Approvals.VerifyJson(myBigGraph.ToJson());
</code></pre>
<p>The diff is done with prettified JSON, so it's super simple to find the property / area that has changed or doesn't work. Knowing the area of the graph should also make it easier to find the usage that is wrong.</p>
<p>There's a vanilla <code>Verify</code> method too, and it saves plain text files. It's useful in cases where you have nice <code>ToString()</code> implementations. Let's try with a &quot;person&quot;:</p>
<pre><code>public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $&quot;{FirstName} {LastName} ({Age})&quot;;
}
}
[Test]
public void Comparing_Objects()
{
var person = new Person
{
FirstName = &quot;Lars-Erik&quot;,
LastName = &quot;Aabech&quot;,
Age = 19
};
Approvals.Verify(person);
}
</code></pre>
<p>It produces the following .received file:</p>
<pre><code>Lars-Erik Aabech (19)
</code></pre>
<p>It can be approved like that.<br />
We can also do lists with &quot;big brother&quot; <code>VerifyAll</code>:</p>
<pre><code>[Test]
public void Comparing_Lists()
{
var list = new List&lt;Person&gt;
{
new Person {FirstName = &quot;Lars-Erik&quot;, LastName = &quot;Aabech&quot;},
new Person {FirstName = &quot;Dear&quot;, LastName = &quot;Reader&quot;}
};
Approvals.VerifyAll(list, &quot;&quot;);
}
</code></pre>
<p>Which, unsurprisingly outputs:</p>
<pre><code>[0] = Lars-Erik Aabech
[1] = Dear Reader
</code></pre>
<p>Now how bout that?<br />
I don't know how quickly you got hooked, but I certainly find it sneaking into more and more of my tests.</p>
<p>It can even do images, but I'll let <a href="http://jamessouth.me/">James</a> blog about that one.</p>
<p>So what are you lingering around here for? Run over to nuget and get your copy, or lurk around some more at the <a href="http://approvaltests.sourceforge.net/">ApprovalTests</a> project site. There's great examples, even though they might not be in your favorite language.</p>
<p>Happy approving! :)</p>
Mon, 23 Oct 2017 20:14:08 Z2017-10-23T20:14:08Z