{"id":2721,"date":"2015-01-07T13:00:32","date_gmt":"2015-01-07T13:00:32","guid":{"rendered":"http:\/\/writeasync.net\/?p=2721"},"modified":"2015-01-05T23:55:37","modified_gmt":"2015-01-05T23:55:37","slug":"fluent-async-testing","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=2721","title":{"rendered":"Fluent async testing"},"content":{"rendered":"<p><a href=\"http:\/\/blog.cellfish.se\/2014\/07\/writing-async-tests.html\">Cellfish writes<\/a>:<\/p>\n<blockquote><p>When .net 4.5 was first in public beta most test frameworks had a bug making it impossible to write async test methods. I wish they had never fixed that because I don&#8217;t think those help you write good tests.<\/p><\/blockquote>\n<p>I completely agree and as a rule I never write async <code>Task<\/code>-returning test methods. The drawback is that doing this carefully means making sure the <code>Task<\/code> is in the desired state, unpacking inner exceptions from an expected <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.aggregateexception(v=vs.110).aspx\"><code>AggregateException<\/code><\/a>, and other tedium.<\/p>\n<p>Really, though, this is a general problem in unit testing, when assertions pile up and become more structural than logical. Consider this sample test for a fictional <code>ItemCounter<\/code>:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic void GivenTwoPrefixMatchesFindByPrefixShouldReturnBothItems()\r\n{\r\n    ItemCounter counter = new ItemCounter();\r\n    counter.Increment(&quot;A1&quot;);\r\n    counter.Increment(&quot;A2&quot;);\r\n    counter.Increment(&quot;A2&quot;);\r\n\r\n    IDictionary&lt;string, int&gt; counts = counter.FindByPrefix(&quot;A&quot;);\r\n\r\n    Assert.AreEqual(2, counts.Count);\r\n    Assert.IsTrue(counts.ContainsKey(&quot;A1&quot;));\r\n    Assert.AreEqual(1, counts&#x5B;&quot;A1&quot;]);\r\n    Assert.IsTrue(counts.ContainsKey(&quot;A2&quot;));\r\n    Assert.AreEqual(2, counts&#x5B;&quot;A2&quot;]);\r\n}\r\n<\/pre>\n<p>While this isn&#8217;t the <em>worst<\/em> possible example, it is indicative of a lot of day-to-day code I see. As the number of still logically-related but structurally different checks increases, these assertions become visual clutter and make tests more tedious and opaque than they need to be.<\/p>\n<p>As a first-level refactoring you can always hide the clutter and take a page from the <a href=\"http:\/\/xunitpatterns.com\/\">xUnit Patterns<\/a> playbook, <a href=\"http:\/\/xunitpatterns.com\/Custom%20Assertion.html\">Custom Assertion<\/a>:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic void GivenTwoPrefixMatchesFindByPrefixShouldReturnBothItems()\r\n{\r\n    ItemCounter counter = new ItemCounter();\r\n    counter.Increment(&quot;A1&quot;);\r\n    counter.Increment(&quot;A2&quot;);\r\n    counter.Increment(&quot;A2&quot;);\r\n\r\n    IDictionary&lt;string, int&gt; counts = counter.FindByPrefix(&quot;A&quot;);\r\n\r\n    Assert.AreEqual(2, counts.Count);\r\n    AssertHasItem(counts, &quot;A1&quot;, 1);\r\n    AssertHasItem(counts, &quot;A2&quot;, 2);\r\n}\r\n\r\nprivate static void AssertHasItem&lt;TKey, TValue&gt;(IDictionary&lt;TKey, TValue&gt; items, TKey key, TValue value)\r\n{\r\n    Assert.IsTrue(items.ContainsKey(key));\r\n    Assert.AreEqual(value, items&#x5B;key]);\r\n}\r\n<\/pre>\n<p>This helps readability quite a bit. However, the style is still not quite &#8220;fluent&#8221; (as in, &#8220;reads like a natural sentence&#8221;) and the error messages are no better than before. You can spot fix those problems, expand your custom assertions to be more generic, and so on. But it&#8217;s not going to be trivial (see e.g. <a href=\"http:\/\/www.owenpellegrin.com\/blog\/testing\/how-do-you-solve-multiple-asserts\/\">Owen Pellegrin wrestle with this problem<\/a>).<\/p>\n<p>Thankfully, it turns out <a href=\"http:\/\/www.dennisdoomen.net\/\">Dennis Doomen<\/a> and others have already solved many of these problems in a low-friction reusable library.<\/p>\n<h3>Enter <a href=\"http:\/\/www.fluentassertions.com\/\">Fluent Assertions<\/a>.<\/h3>\n<p>Using <a href=\"http:\/\/www.fluentassertions.com\/\">Fluent Assertions<\/a>, we can rewrite our test like so:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\/\/ ...\r\nusing FluentAssertions;\r\n\/\/ ...\r\npublic void GivenTwoPrefixMatchesFindByPrefixShouldReturnBothItems()\r\n{\r\n    ItemCounter counter = new ItemCounter();\r\n    counter.Increment(&quot;A1&quot;);\r\n    counter.Increment(&quot;A2&quot;);\r\n    counter.Increment(&quot;A2&quot;);\r\n\r\n    IDictionary&lt;string, int&gt; counts = counter.FindByPrefix(&quot;A&quot;);\r\n\r\n    counts.Should().HaveCount(2).And.Contain(&quot;A1&quot;, 1).And.Contain(&quot;A2&quot;, 2);\r\n}\r\n<\/pre>\n<p>The full assertion now fits comfortably on a single line, is easy to write, and it doesn&#8217;t get much more fluent than this. The error messages are also a breath of fresh air and give exact details of what went wrong:<\/p>\n<ul>\n<li>Wrong count? <code>\"Expected dictionary {[A1, 1]} to have 2 item(s), but found 1.\"<\/code><\/li>\n<li>Missing key? <code>\"Expected dictionary to contain value 2 at key \"A2\", but the key was not found.\"<\/code><\/li>\n<li>Wrong value? <code>\"Expected dictionary to contain value 1 at key \"A1\", but found 4.\"<\/code><\/li>\n<li>It works even if the dictionary itself was <code>null<\/code>: <code>\"Expected 2 item(s), but found &lt;null&gt;.\"<\/code><\/li>\n<\/ul>\n<p>Of course, it does more than just dictionaries; see the <a href=\"https:\/\/github.com\/dennisdoomen\/fluentassertions\/wiki\">Fluent Assertions documentation on GitHub<\/a> for many more examples.<\/p>\n<p>So, jumping back to async and <code>Task<\/code>s &#8212; this is sadly one area that is currently lacking in Fluent Assertions. There is basic support for invoking <code>Func&lt;Task&gt;<\/code> delegates and asserting exception throwing behavior but this has a lot of the same drawbacks alluded to above (e.g. possible test hangs due to calling <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd235635(v=vs.110).aspx\"><code>Wait()<\/code><\/a> on a <code>Task<\/code> that will never complete).<\/p>\n<p>Luckily there is a good <a href=\"https:\/\/github.com\/dennisdoomen\/fluentassertions\/wiki#extensibility\">extensibility experience<\/a> so we can fill in the gaps and write async tests the way we want. Here is my attempt at doing just that: <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/tree\/master\/projects\/FluentSample\">FluentSample on GitHub<\/a>. It contains methods for dealing with <code>Task<\/code> in the style of Fluent Assertions, cutting down on boilerplate and improving readability.<\/p>\n<p>Here are a few examples that show basic assertions on <code>Task<\/code> states:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;TestMethod]\r\npublic void DelayZeroShouldCompleteImmediately()\r\n{\r\n    Task task = Task.Delay(TimeSpan.Zero);\r\n\r\n    task.Should().BeCompletedSuccessfully();\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void DelayInfiniteShouldNeverComplete()\r\n{\r\n    Task task = Task.Delay(Timeout.InfiniteTimeSpan);\r\n\r\n    task.Should().BePending();\r\n}\r\n\r\n&#x5B;TestMethod]\r\npublic void DelayInfiniteWithLaterCancellationShouldBeCanceled()\r\n{\r\n    using (CancellationTokenSource cts = new CancellationTokenSource())\r\n    {\r\n        Task task = Task.Delay(Timeout.InfiniteTimeSpan, cts.Token);\r\n\r\n        cts.Cancel();\r\n\r\n        task.Should().BeCanceled();\r\n    }\r\n}\r\n<\/pre>\n<p>Here is a more advanced example showing how to chain exception verification. Compare this to the &#8220;classic&#8221; way shown below and you can see the real value:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\/\/ Class under test:\r\npublic sealed class ParallelLoop\r\n{\r\n    private readonly Func&lt;int, Task&gt; doAsync;\r\n\r\n    public ParallelLoop(Func&lt;int, Task&gt; doAsync)\r\n    {\r\n        this.doAsync = doAsync;\r\n    }\r\n\r\n    public Task RunAsync(int count)\r\n    {\r\n        List&lt;Task&gt; tasks = new List&lt;Task&gt;();\r\n        for (int i = 0; i &lt; count; ++i)\r\n        {\r\n            tasks.Add(this.doAsync(i));\r\n        }\r\n\r\n        return Task.WhenAll(tasks);\r\n    }\r\n}\r\n\r\n\/\/ Helper factory method\r\nprivate static Func&lt;int, Task&gt; GetThrowFunc()\r\n{\r\n    TaskCompletionSource&lt;bool&gt;&#x5B;] t = new TaskCompletionSource&lt;bool&gt;&#x5B;]\r\n    {\r\n        new TaskCompletionSource&lt;bool&gt;(),\r\n        new TaskCompletionSource&lt;bool&gt;()\r\n    };\r\n    t&#x5B;0].SetException(new InvalidOperationException(&quot;I:0&quot;));\r\n    t&#x5B;1].SetException(new InvalidProgramException(&quot;I:1&quot;));            \r\n    return i =&gt; t&#x5B;i].Task;\r\n}\r\n\r\n\/\/ Fluent way\r\n&#x5B;TestMethod]\r\npublic void RunAsyncAggregatesAllExceptionsAtEndFluent()\r\n{\r\n    ParallelLoop loop = new ParallelLoop(GetThrowFunc());\r\n\r\n    Task task = loop.RunAsync(2);\r\n\r\n    task.Should().BeFaulted().WithException&lt;InvalidOperationException&gt;().WithMessage(&quot;I:0&quot;);\r\n    task.Should().BeFaulted().WithException&lt;InvalidProgramException&gt;().WithMessage(&quot;I:1&quot;);\r\n}\r\n\r\n\/\/ &quot;Classic&quot; way\r\n&#x5B;TestMethod]\r\npublic void RunAsyncAggregatesAllExceptionsAtEndClassic()\r\n{\r\n    ParallelLoop loop = new ParallelLoop(GetThrowFunc());\r\n\r\n    Task task = loop.RunAsync(2);\r\n\r\n    Assert.IsTrue(task.IsFaulted);\r\n    AggregateException exception = task.Exception;\r\n    Assert.IsNotNull(exception);\r\n    Dictionary&lt;string, Exception&gt; exceptions = exception.InnerExceptions.ToDictionary(e =&gt; e.Message);\r\n    Assert.IsTrue(exceptions.ContainsKey(&quot;I:0&quot;));\r\n    Assert.IsInstanceOfType(exceptions&#x5B;&quot;I:0&quot;], typeof(InvalidOperationException));\r\n    Assert.IsTrue(exceptions.ContainsKey(&quot;I:1&quot;));\r\n    Assert.IsInstanceOfType(exceptions&#x5B;&quot;I:1&quot;], typeof(InvalidProgramException));\r\n}\r\n<\/pre>\n<p>Feel free to try out and modify or extend FluentSample as you see fit. Happy async testing!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cellfish writes: When .net 4.5 was first in public beta most test frameworks had a bug making it impossible to write async test methods. I wish they had never fixed that because I don&#8217;t think those help you write good&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,41],"tags":[],"class_list":["post-2721","post","type-post","status-publish","format-standard","hentry","category-async","category-tdd"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/2721","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2721"}],"version-history":[{"count":0,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/2721\/revisions"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2721"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2721"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2721"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}