{"id":2111,"date":"2017-06-19T16:49:11","date_gmt":"2017-06-19T20:49:11","guid":{"rendered":"http:\/\/blog.bitsofgenius.com\/?p=2111"},"modified":"2017-06-21T12:49:19","modified_gmt":"2017-06-21T16:49:19","slug":"a-common-template-approach-to-iterative-tests-in-nunit-3-0-and-microsoft-unit-tests","status":"publish","type":"post","link":"https:\/\/blog.bitsofgenius.com\/?p=2111","title":{"rendered":"A Common Template Approach to Iterative Tests in NUnit 3.0 and Microsoft Unit Tests"},"content":{"rendered":"<p>NUnit and Microsoft Unit Tests\u00a0use an approach of attributes on a method to identify a test which is expected to throw an exception, to differentiate tests which are expected to not throw an exception and just report success or failure based on Assertions (AreEqual, IsFalse, IsTrue, etc). \u00a0For individual tests, this approach is very effective.<\/p>\n<p>Recently, I wrote\u00a0two complex classes which required iterative testing on sets of parameters. \u00a0One project at work used some complex logic to evaluate arguments. \u00a0This required over 80 lines of parameter combinations to be validated. \u00a0This project used the Microsoft Unit Test architecture.<\/p>\n<p>The other project was a personal project for a Url parser which was designed to validate patterns used by the HttpListener object and be less restrictive on\u00a0content in the host name and other items. \u00a0This project uses NUnit 3.0. \u00a0Both of these projects needed iterative testing to cover the many variations in behavior from combinations of parameters. \u00a0And the results could be either assertions, or expected exceptions.<\/p>\n<p>The example here is for NUnit. \u00a0Microsoft Unit Tests use attributes for similar iteration. \u00a0The CSV parsing in the NUnit tester is handled by a handy assembly named CsvReader available via NuGet. \u00a0And the CSV or TSV file defining the iteration tests is copied to the output folder, where the test assembly expects it.<\/p>\n<p>This <a href=\"https:\/\/github.com\/rambotech\/BOG.Framework\/blob\/master\/Framework_NUnit\/UrlTestData.tsv\" target=\"_blank\" rel=\"noopener\">file<\/a> contains the test data definition. There are some key columns in this file which makes it a good template:<\/p>\n<ul>\n<li>The DataRow column: similar to the Microsoft Unit Test architecture, this column marks the row of the test for use in output to identify any row with problems.<\/li>\n<li>Any column containing {empty} represents an empty string to reduce ambiguity.<\/li>\n<li>The column ThrowsException is either {empty} to identify tests expected to be exception-free and verified with Assert.*(), or contains the class name of the Exception expected.<\/li>\n<li>ExceptionContains is a further test of an an exception: it can be {empty}, or be a string expected to be contained in the exception message text to further qualify the exception expected.<\/li>\n<li>Notes is a column not used in the test: it is to help the poor developer who can&#8217;t decrypt your specific intent from the test properties.<\/li>\n<\/ul>\n<p>All other columns are properties for the test. \u00a0Columns containing expected results will sometimes appear as &#8220;n\/a&#8221; because an exception is expected, so the &#8220;n\/a&#8221; is a semaphore that an exception occurs in test.<\/p>\n<p>This <a href=\"https:\/\/github.com\/rambotech\/BOG.Framework\/blob\/master\/Framework_NUnit\/UrlTest.cs\" target=\"_blank\" rel=\"noopener\">file<\/a> contains the test class for the UrlTest, which uses the TSV file as its source.<\/p>\n<p>Line 16 Contains the TestData class which provides the test properties to the iterative test method at line 59. \u00a0The properties are loaded into a StringDictionary object, which uses the column name (or property name) as the key for its value.<\/p>\n<p>The test method at line 59 converts the content to local variables, then uses an if() statement to determine how to\u00a0call the work method: expecting an error, or not. \u00a0In case there is a need to debug a single line of the TSV (i.e. a specific test case), the commented code in lines 86-89 can be used, by adjusting the targeted line&#8217;s DataRow value to the test parameters to debug.<\/p>\n<p>That&#8217;s about it. \u00a0This method can be easily adjusted for other iterative tests. \u00a0The beauty of it is that is can used to test many arguments\/one answer, or many arguments\/many answers, and also handle exceptions thrown regardless of expectation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>NUnit and Microsoft Unit Tests\u00a0use an approach of attributes on a method to identify a test which is expected to throw an exception, to differentiate tests which are expected to not throw an exception and just report success or failure based on Assertions (AreEqual, IsFalse, IsTrue, etc). \u00a0For individual tests, this approach is very effective. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14,16,13,17,27],"tags":[],"class_list":["post-2111","post","type-post","status-publish","format-standard","hentry","category-dotnetclasslibraries","category-dotnettechniques","category-technologythoughts","category-tips-and-tricks","category-visual-studio"],"_links":{"self":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts\/2111","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2111"}],"version-history":[{"count":13,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts\/2111\/revisions"}],"predecessor-version":[{"id":2125,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=\/wp\/v2\/posts\/2111\/revisions\/2125"}],"wp:attachment":[{"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2111"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2111"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bitsofgenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2111"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}