{"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=\"span-reading-time 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    &#x5B;'{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('&#x5B;%f + %fi]', &#x5B;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')&#x5B;'real'] as ComplexNumberInspector).Real(a);\r\n    (Inspect('Complex Number')&#x5B;'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 data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" 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\" \/><\/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=\"span-reading-time 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_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4],"tags":[292,51],"class_list":["post-2095","post","type-post","status-publish","format-standard","hentry","category-delphi","tag-delphi","tag-smoketest"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-xN","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2095","targetHints":{"allow":["GET"]}}],"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}]}}