{"id":2095,"date":"2013-11-05T21:57:27","date_gmt":"2013-11-05T09:57:27","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2095"},"modified":"2013-11-05T21:58:14","modified_gmt":"2013-11-05T09:58:14","slug":"extending-smoketest-part-1-an-inspector-calls","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2095\/","title":{"rendered":"Extending Smoketest (Part 1) &#8211; An Inspector Calls"},"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>In the soon to be released <strong>Smoketest<\/strong> 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 <strong>Smoketest<\/strong> framework.<\/p>\n<p><!--more--><\/p>\n<p>In a real-world scenario this may be any complex business object type which we have a set of tests we regularly apply in addition to the tests we may perform on fundamental types using the tests built-in to <strong>Smoketest<\/strong> itself.<\/p>\n<p>For this exercise I shall use the example of a <strong>Complex Number<\/strong> test extension that was implemented specifically to exercise this aspect of <strong>Smoketest<\/strong> as part of it&#8217;s own self-test test suite.<\/p>\n<p>First let us define the <strong>ComplexNumber<\/strong> type that we wish to test:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  type\r\n    TComplexNumber = record\r\n      Real: Double;\r\n      Imaginary: Double;\r\n    end;\r\n<\/pre>\n<p>As well as test extensions we can also implement custom inspectors.<\/p>\n<p>These are a little more straightforward than tests but the process involved is fundamentally similar so by way of a gentle introduction let&#8217;s do an inspector first.<\/p>\n<p>The first step is to define an interface for our inspector.<\/p>\n<p>There are no requirements for this interface other than it should have an <strong>IID<\/strong> and define the methods we will provide for inspecting our custom type.  The convention in <strong>Smoketest<\/strong> is to have a <strong>Value()<\/strong> method for inspecting the type, but we can define whatever methods we feel appropriate.<\/p>\n<p>In this case I define three methods:  <strong>Value()<\/strong> plus two further methods for inspecting the <strong>Real()<\/strong> and <strong>Imaginary()<\/strong> components of a <strong>TComplexNumber<\/strong> individually:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n    ComplexNumberInspector = interface\r\n    ['{614CD588-718D-4950-8A5C-7F8252613487}']\r\n      procedure Real(aValue: TComplexNumber);\r\n      procedure Imaginary(aValue: TComplexNumber);\r\n      procedure Value(aValue: TComplexNumber);\r\n    end;\r\n<\/pre>\n<p>Now we need to declare a class that implements this interface.  This class must extend the <strong>TInspector<\/strong> class from <strong>Smoketest<\/strong>.<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n    TComplexNumberInspector = class(TInspector, ComplexNumberInspector)\r\n    public\r\n      procedure Real(aValue: TComplexNumber);\r\n      procedure Imaginary(aValue: TComplexNumber);\r\n      procedure Value(aValue: TComplexNumber);\r\n    end;\r\n<\/pre>\n<p>We register this extension class with <strong>Smoketest<\/strong> in the unit <strong>initialization<\/strong> section.  This is the only occasion that the class needs to be referenced so by registering it from within the unit itself the entire class implementation can be kept in, well, the <em>implementation<\/em> section.  Only the interface needs to be in, well, the <em>interface<\/em> section.<\/p>\n<p>Registering a custom inspector with <strong>Smoketest<\/strong> is achieved by calling the <strong>RegisterExtension()<\/strong> method identifying the interface and the implementing class:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\ninitialization\r\n  Smoketest.RegisterExtension(ComplexNumberInspector, TComplexNumberInspector);\r\nend.\r\n<\/pre>\n<p>Now to implement the inspector methods themselves.<\/p>\n<p>An inspector method simply <strong>Emit()<\/strong>s some string which is captured by <strong>Smoketest<\/strong> for presentation in the test results, so each of the <strong>ComplexNumberInspector<\/strong> methods has a very simple job of emitting a suitably presented string.<\/p>\n<p>In addition to these methods I also implement a helper routine to format an entire ComplexNumber as a string which will come in handy later when I also implement the test extension.<\/p>\n<p><strong>NOTE:<\/strong> <em>A simple function is used for this helper rather than a record helper since this is part of self-test code which must be compatible with Delphi versions as far back as <strong>Delphi 7<\/strong><\/em>.<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  function ComplexNumberToString(const aValue: TComplexNumber): String;\r\n  begin\r\n    result := Format('[%f + %fi]', [aValue.Real, aValue.Imaginary]);\r\n  end;\r\n\r\n\r\n  procedure TComplexNumberInspector.Real(aValue: TComplexNumber);\r\n  begin\r\n    Emit(FloatToStr(aValue.Real));\r\n  end;\r\n\r\n  procedure TComplexNumberInspector.Imaginary(aValue: TComplexNumber);\r\n  begin\r\n    Emit(FloatToStr(aValue.Imaginary));\r\n  end;\r\n\r\n  procedure TComplexNumberInspector.Value(aValue: TComplexNumber);\r\n  begin\r\n    Emit(ComplexNumberToString(aValue));\r\n  end;\r\n<\/pre>\n<p>That&#8217;s it.<\/p>\n<p>With this inspector interface defined, the class implemented and both registered as an extension with <strong>Smoketest<\/strong>, I can now inspect <strong>ComplexNumber<\/strong> values in any of my test cases by obtaining the required extension from an inspector (obtained via a call to the <strong>Inspect()<\/strong> method, you may recall):<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  var\r\n    a: TComplexNumber;\r\n  begin\r\n    a.Real      := 1;\r\n    a.Imaginary := 4;\r\n\r\n    (Inspect('Complex Number') as ComplexNumberInspector).Value(a);\r\n    (Inspect('Complex Number')['real'] as ComplexNumberInspector).Real(a);\r\n    (Inspect('Complex Number')['imaginary'] as ComplexNumberInspector).Imaginary(a);\r\n  end;\r\n<\/pre>\n<p>This can get a bit &#8220;wordy&#8221; and cumbersome though could still represent a benefit for more complex custom types than this simple ComplexNumber example.  Never-the-less I have some ideas for allowing this to be simplified further (the fundamentals of which are already working though the sub-indexed label feature is presenting a challenge which I am currently working on).<\/p>\n<p>Anyhow, for now (and, it is hoped, with the simplified version in the future) the resulting output from these inspections looks like this:<\/p>\n<figure id=\"attachment_2098\" aria-describedby=\"caption-attachment-2098\" style=\"width: 271px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-05-at-22.32.50-.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-05-at-22.32.50-.png?resize=271%2C107&#038;ssl=1\" alt=\"Inspecting Complex Numbers\" width=\"271\" height=\"107\" class=\"size-full wp-image-2098\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-2098\" class=\"wp-caption-text\">Inspecting Complex Numbers<\/figcaption><\/figure>\n<p>In the next post I&#8217;ll show how the same basic approach (with a small twist) allows me to add more useful extensions that create new expectations which can be used to test values of such custom types.<\/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> 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.<\/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,51],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-xN","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2144,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2144\/","url_meta":{"origin":2095,"position":0},"title":"Smoketest 1.0 &#8211; Release and Be Damned!","date":"14 Nov 2013","format":false,"excerpt":"As I have been promising for some time (quite literally 5 years (!), I am ashamed to admit) I am finally unclenching and releasing the Smoketest framework into the wild, ready or not. The code is published and will continue to be updated in a github repository. Documentation is still\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-14-at-19.51.57-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2111,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2111\/","url_meta":{"origin":2095,"position":1},"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":2956,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2956\/","url_meta":{"origin":2095,"position":2},"title":"Introducing: Smoketest 2.0","date":"24 Sep 2019","format":false,"excerpt":"Meet the new Smoketest. Nothing like the old Smoketest. A new, ultra-lightweight unit testing framework with xUnit 2.x result output (and the ability to support other output formats as needed).","rel":"","context":"In &quot;automation&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/vape_2.jpg?fit=1200%2C307&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2164,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2164\/","url_meta":{"origin":2095,"position":3},"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":2185,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2185\/","url_meta":{"origin":2095,"position":4},"title":"Smoketest &#8211; Performance Case Visualisations","date":"22 Nov 2013","format":false,"excerpt":"This post is a peek behind the curtain of the next major update to Smoketest which I hope to have completed shortly: Performance Case visualisations. Smoketest has always had two types of test case that you could implement by deriving from two distinct base classes: TTestCase is the base class\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.57.52.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2006,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2006\/","url_meta":{"origin":2095,"position":5},"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":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2095"}],"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=2095"}],"version-history":[{"count":7,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2095\/revisions"}],"predecessor-version":[{"id":2103,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2095\/revisions\/2103"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2095"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2095"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2095"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}