{"id":5394,"date":"2018-02-11T13:00:38","date_gmt":"2018-02-11T13:00:38","guid":{"rendered":"http:\/\/writeasync.net\/?p=5394"},"modified":"2018-02-01T04:42:05","modified_gmt":"2018-02-01T04:42:05","slug":"loopy-tests","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=5394","title":{"rendered":"Loopy tests"},"content":{"rendered":"<p>Loops are fundamental structures in almost every programming language (we&#8217;ll put aside <a href=\"https:\/\/en.wikipedia.org\/wiki\/APL_(programming_language)#Design\">APL<\/a> for the time being). In unit tests, however, <a href=\"https:\/\/stackoverflow.com\/questions\/27880631\/\">loops<\/a> can be a <a href=\"https:\/\/softwareengineering.stackexchange.com\/questions\/197313\/\">problem<\/a>. This is especially true of loops in the &#8220;Assert&#8221; section. For instance, consider this seemingly innocuous test of an integer range object:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nvoid TestSubtractAllEvens()\r\n{\r\n    \/\/ Arrange\r\n    const int N = 8;\r\n    Range range = new Range(1, N);\r\n\r\n    \/\/ Act\r\n    for (int i = 1; i &lt;= N \/ 2; ++i)\r\n    {\r\n        range.Subtract(new Range(2 * i, 2 * i));\r\n    }\r\n\r\n    \/\/ Assert\r\n    \/\/ (Range implements IEnumerable&lt;int&gt;)\r\n    foreach (int i in range)\r\n    {\r\n        Assert.IsTrue(i % 2 == 1, &quot;{0} should be odd&quot;, i);\r\n    }\r\n}\r\n<\/pre>\n<p>With a bit of studying, we can easily work out what is happening in this test. The problem is that it is not actually doing its job! I am not saying the test is functionally incorrect &#8212; just that <strong>it will not reveal plausible defects in the system under test<\/strong>. Imagine these cases:<\/p>\n<ul>\n<li><code>Range<\/code> mistakenly returned an empty sequence from its enumerator. The test would still pass (because no assertions would run).<\/li>\n<li><code>Range<\/code> had some sort of off-by-one error (too few or too many elements). The test would still pass (because no explicit boundaries are checked).<\/li>\n<\/ul>\n<p>You could come up with several spot fixes for these problems. Maybe you could use a library to replace explicit Assert loops with <a href=\"http:\/\/fluentassertions.com\/documentation.html#collections\">clearer and stricter collection checks<\/a>. Maybe you add a counter and fail if zero items were found.<\/p>\n<p>In my experience, there is usually a simpler solution &#8212; just get rid of the loop and be explicit! Not only is this immune to the common problems above, it improves readability and helps show what the object is really expected to do:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nvoid TestSubtractAllEvens()\r\n{\r\n    \/\/ Arrange\r\n    Range range = new Range(1, 8);\r\n\r\n    \/\/ Act\r\n    range.Subtract(new Range(2, 2));\r\n    range.Subtract(new Range(4, 4));\r\n    range.Subtract(new Range(6, 6));\r\n    range.Subtract(new Range(8, 8));\r\n\r\n    \/\/ Assert\r\n    \/\/ (ToArray() from System.Linq)\r\n    int&#x5B;] items = range.ToArray();\r\n\r\n    Assert.AreEqual(1, items&#x5B;0]);\r\n    Assert.AreEqual(3, items&#x5B;1]);\r\n    Assert.AreEqual(5, items&#x5B;2]);\r\n    Assert.AreEqual(7, items&#x5B;3]);\r\n}\r\n<\/pre>\n<p>Some would bristle at these nakedly <a href=\"https:\/\/en.wikipedia.org\/wiki\/Loop_unrolling\">unrolled loops<\/a>, but there is clear value in being explicit in such behavioral specifications (being <a href=\"http:\/\/arlobelshee.com\/wet-when-dry-doesnt-apply\/\">&#8220;WET&#8221;<\/a> as Arlo Belshee would put it). If you still find yourself going to a &#8220;loopy&#8221; place &#8212; especially if you want more exhaustive verification than this sort of hand-rolled &#8220;spec by example&#8221; approach could scale to &#8212; you might be better off with <a href=\"http:\/\/blog.jessitron.com\/2013\/04\/property-based-testing-what-is-it.html\">property-based testing<\/a> a la <a href=\"https:\/\/fscheck.github.io\/FsCheck\/\">FsCheck<\/a>. At least with that strategy the loops are hidden within the test engine and the generators, no longer taking up valuable conceptual real estate. After all, that is what it&#8217;s all about &#8212; getting as close as you can to the essence of expectations vs. reality vis-\u00e0-vis your code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Loops are fundamental structures in almost every programming language (we&#8217;ll put aside APL for the time being). In unit tests, however, loops can be a problem. This is especially true of loops in the &#8220;Assert&#8221; section. For instance, consider this&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51],"tags":[],"class_list":["post-5394","post","type-post","status-publish","format-standard","hentry","category-testing"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5394","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=5394"}],"version-history":[{"count":1,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5394\/revisions"}],"predecessor-version":[{"id":5395,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5394\/revisions\/5395"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5394"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5394"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5394"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}