{"id":2193,"date":"2013-11-25T13:09:31","date_gmt":"2013-11-25T01:09:31","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2193"},"modified":"2013-11-25T16:39:25","modified_gmt":"2013-11-25T04:39:25","slug":"dunit-compatibility-in-smoketest","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2193\/","title":{"rendered":"DUnit Compatibility in Smoketest"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>To address some odd concerns about differences between <strong>DUnit<\/strong> and <strong>Smoketest<\/strong>, I thought it would be useful to demonstrate how it is entirely within the gift of a <strong>Smoketest<\/strong> user to create their own &#8220;comfort&#8221; layer, to make using <strong>Smoketest<\/strong> more similar to the <strong>DUnit<\/strong> framework if they wish (though why in that case they wouldn&#8217;t simply use <strong>DUnit<\/strong>, I can&#8217;t quite fathom.  But still).<\/p>\n<p><!--more--><\/p>\n<h2>Interfaces <del>vs<\/del> and Inheritance<\/h2>\n<p>The great thing about <strong>Delphi<\/strong> is that when a class implements an <strong>interface<\/strong> any derived classes <em>inherit<\/em> that implementation.  Even better, if you implement the interface methods virtually, then those derived classes can override the implementation <em>without having to redeclare the interface<\/em>.<\/p>\n<p>So, if you really don&#8217;t like the idea of declaring and implementing interfaces to provide <strong>Setup<\/strong> and <strong>Cleanup<\/strong> methods on <strong>TTestCase<\/strong> derived classes you have a very simple remedy:<\/p>\n<p><em>Implement your own class from which to derive your test cases and have this provide default, no-op, virtual implementations of these framework hooks, just as the <strong>DUnit<\/strong> test case base class does for the virtual methods it relies on.<\/em><\/p>\n<p>If it really bothers you <em>that<\/em> much, you can even take this opportunity to replace the framework <strong>Cleanup<\/strong> method with a virtual <strong>TearDown<\/strong> method in it&#8217;s stead.<\/p>\n<p>Heck, if you really want to write <strong>CheckEquals()<\/strong> for your test and then describe it separately, you can even provide <em>that<\/em> in your <strong>DUnit<\/strong> comfortable compatibility class.  You can even have the <strong>CheckEquals()<\/strong> method always &#8220;fail early&#8221;, just like <strong>DUnit<\/strong>.<\/p>\n<p>You might end up with something like this:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  type\r\n    TDUnitTestCase = class(TTestCase, ISetupTestCase,\r\n                                      ICleanupTestCase)\r\n    protected\r\n      procedure Setup; virtual;\r\n      procedure Cleanup;\r\n      procedure TearDown; virtual;\r\n      procedure CheckEquals(aResult: Boolean; aMsg: String = '');\r\n    end;\r\n\r\n\r\n  procedure TDUnitTestCase.Setup;\r\n  begin\r\n    \/\/ NO-OP - just like mother used to make\r\n  end;\r\n\r\n  procedure TDUnitTestCase.Cleanup;\r\n  begin\r\n    TearDown;\r\n  end;\r\n\r\n  procedure TDUnitTestCase.TearDown;\r\n  begin\r\n    \/\/ NO-OP - the comfort of the familiar\r\n  end;\r\n\r\n  procedure TDUnitTestCase.CheckEquals(aResult: Boolean; aMsg: String);\r\n  begin\r\n    Test(aMsg).Expect(aResult).IsTRUE.IsShowStopper;\r\n  end;\r\n<\/pre>\n<p>You could even implement the same pattern for the project and method specific setup and cleanup hooks.  <strong>DUnit<\/strong> doesn&#8217;t have these so doesn&#8217;t provide a &#8220;specification&#8221; to comply with in this area, but you would presumably choose to follow the same virtual method pattern that <strong>DUnit<\/strong> adopts for the test case setup\/teardown.<\/p>\n<h2>Why Not Just Copy DUnit ?<\/h2>\n<p>Some people don&#8217;t seem to understand why I didn&#8217;t simply mindlessly copy <strong>DUnit<\/strong>, thus making such adaptations unnecessary.<\/p>\n<p>The thing is, I didn&#8217;t implement <strong>Smoketest<\/strong> with compatibility with <strong>DUnit<\/strong> in mind.  Quite the opposite.  I didn&#8217;t <em>like<\/em> the way <strong>DUnit<\/strong> worked!  Little wonder then that <strong>Smoketest<\/strong> works differently!<\/p>\n<p>I like to think however that the approach I took demonstrates it&#8217;s benefits no more obviously than when considering the ease with which it is possible to build such alternative API&#8217;s on top of the framework without having to resort to modifying the framework itself.<\/p>\n<p>I imagine &#8211; though I have not tried &#8211; that you could even contrive to make such a compatibility layer so seamless and complete that an existing <strong>DUnit<\/strong> project would compile directly against <strong>Smoketest<\/strong> with only the slightest of changes, primarily around not having to explicitly state which &#8220;runner&#8221; you are using, since in <strong>Smoketest<\/strong> this is governed by the project compiler settings\/directive directly (console vs GUI app) without the need for parallel changes to the project source code.<\/p>\n<p>In the case of <strong>DUnit<\/strong>, it was impossible to achieve the changes in the test writing experience that I desired; you cannot <em>reduce<\/em> the visibility of inherited members, so with any pattern that relies on visibility of certain inherited class members you are stuck with the choices made in the implementation of the base class.<\/p>\n<p><strong>Smoketest<\/strong> doesn&#8217;t paint you into this particular corner.<\/p>\n<p>For people &#8211; such as myself &#8211; who don&#8217;t like <strong>DUnit<\/strong>, <strong>Smoketest<\/strong> exists precisely to offer a choice.  A choice albeit initially only I was making (in the sense of deciding how my preferred alternative would work differently).<\/p>\n<p>If someone doesn&#8217;t like the choices <em>I<\/em> made it seems to me they themselves have a choice:  They can &#8211; with a modicum of thought and a minimum of effort &#8211; add a layer of comfort that they will find acceptable.  Or they can choose simply not to use <strong>Smoketest<\/strong>.<\/p>\n<p><em>I<\/em> would not presume to tell <em>them<\/em> what choice <em>they<\/em> should make.  \ud83d\ude09<\/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\">3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> 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 &#8220;comfort&#8221; layer, to make using Smoketest more similar to the DUnit framework if they wish (though why in that case they [&hellip;]<\/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":[292,50,51],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-zn","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2177,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2177\/","url_meta":{"origin":2193,"position":0},"title":"Smoketest &#8211; Interfaces vs Inheritance","date":"22 Nov 2013","format":false,"excerpt":"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\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.02.29.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2164,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2164\/","url_meta":{"origin":2193,"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":2193,"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":263,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/263\/","url_meta":{"origin":2193,"position":3},"title":"Exchange() &#8211; Epilogue and Smoketest","date":"28 Aug 2008","format":false,"excerpt":"A colleague of mine directed me to a further minor refinement of the 'final' Exchange() code I posted the other day. The change is minor but yields a worthwhile performance improvement, but my main reason for bothering to post (yet!) another update is an excuse to introduce the testing framework\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/exchange_after-295x300.gif?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2095,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2095\/","url_meta":{"origin":2193,"position":4},"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":[]},{"id":2111,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2111\/","url_meta":{"origin":2193,"position":5},"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":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2193"}],"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=2193"}],"version-history":[{"count":8,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2193\/revisions"}],"predecessor-version":[{"id":2201,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2193\/revisions\/2201"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}