{"id":2177,"date":"2013-11-22T08:38:25","date_gmt":"2013-11-21T20:38:25","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2177"},"modified":"2013-11-22T09:01:04","modified_gmt":"2013-11-21T21:01:04","slug":"interfaces-vs-inheritance","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2177\/","title":{"rendered":"Smoketest &#8211; Interfaces vs Inheritance"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>A couple of commenters on my previous post have taken issue with my use of interfaces to form contracts between test cases and the test framework, rather than using simple virtual methods and inheritance as found in <strong>DUnit<\/strong>.  I thought it would be interesting to illustrate why I went down this route.<\/p>\n<p><!--more--><\/p>\n<p>It is suggested that using inheritance means that discovering the methods required to be implemented to support <strong>Setup<\/strong>\/<strong>TearDown<\/strong> in <strong>DUnit<\/strong> is simpler than having to know the required interface name and then having to lookup the method signature involved, as in the case of <strong>Smoketest<\/strong>.<\/p>\n<p>This of course assumes that documentation either does not exist or is being wilfully ignored.<\/p>\n<p>First of all, the interfaces in <strong>Smoketest<\/strong> are designed to be highly memorable.  They read as a natural description of a behaviour that a test case class follows.<\/p>\n<p>Where a test case has some project setup to perform it states &#8220;<em>I Setup (the) Project<\/em>&#8220;.  If it has some setup to perform specific to the test case &#8220;<em>I Setup (this) Test Case<\/em>&#8220;.  And so on.<\/p>\n<p>So remembering the interface name really shouldn&#8217;t be hard, since it describes what you wish to achieve.  Whether you intuit the interface name, select it from the fairly short list of interfaces offered by IDE Code Completion, the chances are you will only have to look it up once.<\/p>\n<p>The compiler then ensures that the contract is fulfilled.<\/p>\n<p>So let&#8217;s assume you at least know the interface involved and look at the impact that using the interface has on finding the method(s) required, as compared to finding the method from the list of inherited methods.<\/p>\n<p>First, let&#8217;s look at what the IDE offers when we invoke <strong>Code Completion<\/strong> in the class declaration of a <strong>DUnit<\/strong> test case.<\/p>\n<figure id=\"attachment_2178\" aria-describedby=\"caption-attachment-2178\" style=\"width: 382px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.08.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.08.png?resize=382%2C607&#038;ssl=1\" alt=\"Page 1 of 3\" width=\"382\" height=\"607\" class=\"size-full wp-image-2178\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.08.png?w=382&amp;ssl=1 382w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.08.png?resize=188%2C300&amp;ssl=1 188w\" sizes=\"(max-width: 382px) 100vw, 382px\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-2178\" class=\"wp-caption-text\">Page 1 of 3<\/figcaption><\/figure>\n<p>I made my suggestion list popup as big as I possibly could, but they aren&#8217;t on even the first page of this greatly enlarged list so scrolling down to the next complete page of suggestions &#8230;.<\/p>\n<figure id=\"attachment_2179\" aria-describedby=\"caption-attachment-2179\" style=\"width: 382px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.29.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.29.png?resize=382%2C602&#038;ssl=1\" alt=\"Page 2 of 3\" width=\"382\" height=\"602\" class=\"size-full wp-image-2179\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.29.png?w=382&amp;ssl=1 382w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.29.png?resize=190%2C300&amp;ssl=1 190w\" sizes=\"(max-width: 382px) 100vw, 382px\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-2179\" class=\"wp-caption-text\">Page 2 of 3<\/figcaption><\/figure>\n<p>Aha &#8211; there they are.  <strong>Setup<\/strong> and <strong>Teardown<\/strong>.  Look carefully, you will see them eventually.<\/p>\n<p>Of course, to find them you have to already know what you are looking for since they are simply listed alphabetically among all the other methods that you have no intention of implementing yourself.<\/p>\n<p>I don&#8217;t see any methods for performing per-test or project-wide setup\/teardown though, so I am not entirely sure of the context in which <strong>Setup<\/strong> and <strong>Teardown<\/strong> operate.  I guess I&#8217;ll have to read the documentation after all.  \ud83d\ude42<\/p>\n<p>Now let&#8217;s look at the same <strong>Code Completion<\/strong> offering for a <strong>Smoketest<\/strong> test case class declaration:<\/p>\n<figure id=\"attachment_2180\" aria-describedby=\"caption-attachment-2180\" style=\"width: 362px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.04.18.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.04.18.png?resize=362%2C101&#038;ssl=1\" alt=\"TOP OF THE BILL!\" width=\"362\" height=\"101\" class=\"size-full wp-image-2180\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.04.18.png?w=362&amp;ssl=1 362w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.04.18.png?resize=300%2C83&amp;ssl=1 300w\" sizes=\"(max-width: 362px) 100vw, 362px\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-2180\" class=\"wp-caption-text\">TOP OF THE BILL!<\/figcaption><\/figure>\n<p>Well, would you look at that.  The methods introduced by my specified interface contracts (in this case, that I will provide a name for the case and that I perform some test case setup), are put front and centre.<\/p>\n<p>Now, without the interfaces declared on the class, I would see no such methods at all.  But is this really a problem ?  I don&#8217;t think so.  The list of available methods is itself much shorter and the complete lack of any setup\/cleanup methods will itself remind me that I have to form my contract with the framework first.<\/p>\n<p>And I then get to choose <strong>which<\/strong> contract &#8211; setting up specific test methods, the test case or project wide setup (and\/or attendant cleanup).<\/p>\n<p>On that note, issue has also been taken with the use of &#8220;<em>Cleanup<\/em>&#8221; rather than &#8220;<em>TearDown<\/em>&#8221; in the method and interface names.  This was deliberate.  <strong>TearDown<\/strong> has a finality about it that seemed to be most often not appropriate.<\/p>\n<p>Take for example the JSON test case which performs some cleanup after every test method by clearing a JSON object in preparation for the next test method.  This really isn&#8217;t &#8220;tearing anything down&#8221;.  It really <strong>is<\/strong> just &#8220;cleaning up&#8221;.<\/p>\n<p>Sometimes your cleanup <em>will<\/em> involve completely destroying some artefacts used\/created during testing, but this is just one extreme form of cleanup.<\/p>\n<p>When the house-work needs doing, do you tear down your house or do you clean it up ?<\/p>\n<p>Even tearing down the house, if\/when it ever becomes necessary, is only what you do in order to &#8220;clean&#8221; the site, in readiness for a new house to be built.<\/p>\n<p><strong>TearDown<\/strong> may appeal to some people&#8217;s sense of the dramatic, but other than that it is actually a very poor choice of name imho.<\/p>\n<p>I didn&#8217;t create <strong>Smoketest<\/strong> in order to simply create a plug-compatible alternative to <strong>DUnit<\/strong>.  I didn&#8217;t even create it as a &#8220;competitor&#8221; for <strong>DUnit<\/strong>.  I created it for myself precisely because I didn&#8217;t and don&#8217;t like the way <strong>DUnit<\/strong> works.<\/p>\n<p>And because <strong>DUnit<\/strong> didn&#8217;t and doesn&#8217;t do some things that I wanted and needed (see my next post).<\/p>\n<p>I simply choose now to share it.<\/p>\n<p>If you do like the way <strong>DUnit<\/strong> works you are of course more than welcome to continue using it, but don&#8217;t expect <strong>Smoketest<\/strong> to conform to any expectations you might bring from that other framework.  \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> A couple of commenters on my previous post have taken issue with my use of interfaces to form contracts between test cases and the test framework, rather than using simple virtual methods and inheritance as found in DUnit. I thought it would be interesting to illustrate why I went down this route.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[261,51],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-z7","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2193,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2193\/","url_meta":{"origin":2177,"position":0},"title":"DUnit Compatibility in Smoketest","date":"25 Nov 2013","format":false,"excerpt":"To address some odd concerns about differences between DUnit and Smoketest, I thought it would be useful to demonstrate how it is entirely within the gift of a Smoketest user to create their own \"comfort\" layer, to make using Smoketest more similar to the DUnit framework if they wish (though\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2164,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2164\/","url_meta":{"origin":2177,"position":1},"title":"Smoketest &#8211; Some Differences With DUnit","date":"19 Nov 2013","format":false,"excerpt":"Writing tests in Smoketest is intended to enable a test developer to write tests in a way that describe themselves, without requiring the test developer to add this \"narrative\" themselves. To see this in action, I thought I would compare some simple DUnit tests with the equivalent using the Smoketest\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2169,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2169\/","url_meta":{"origin":2177,"position":2},"title":"Smoketest &#8211; Set Me Up \/ Tear Me Down","date":"20 Nov 2013","format":false,"excerpt":"In a previous post I demonstrated how the default \"pretty name\" for a Smoketest test case (derived from the test case classname) can be over-ridden by a test developer by implementing a specific interface (INameCase) on the test case class itself. There are some other interfaces that can be implemented\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2111,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2111\/","url_meta":{"origin":2177,"position":3},"title":"Extending Smoketest (Part 2) &#8211; Great(er) Expectations","date":"07 Nov 2013","format":false,"excerpt":"In my previous post on Smoketest I showed how you can extend the inspections framework to work with complex types in your code. As promised, I shall now show how you can do much the same thing to extend the framework with entirely new tests. \u201cI must be taken as\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2006,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2006\/","url_meta":{"origin":2177,"position":4},"title":"Blowing Smoke&#8230;","date":"01 Nov 2013","format":false,"excerpt":"To alleviate the grind of polishing and sanitising my code (and, let's be honest, just plain 'fixing' it in some cases) ready for release, I have re-kindled my participation on Stack Overflow. In a happy confluence yesterday a question came up which allowed me to exercise one of the libraries\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"Ready to Run","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2095,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2095\/","url_meta":{"origin":2177,"position":5},"title":"Extending Smoketest (Part 1) &#8211; An Inspector Calls","date":"05 Nov 2013","format":false,"excerpt":"In the soon to be released Smoketest framework it is sometimes useful to create new test types to supplement the tests built-in to the framework. In this and the next post I will walk through the process of implementing and registering a custom test with the Smoketest framework. In a\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2177"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=2177"}],"version-history":[{"count":5,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2177\/revisions"}],"predecessor-version":[{"id":2189,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2177\/revisions\/2189"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}