{"id":1758,"date":"2014-02-06T00:00:00","date_gmt":"2014-02-06T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/acceptance-testing-with-fitnesse-multiplicities-and-comparisons\/"},"modified":"2021-05-11T15:56:13","modified_gmt":"2021-05-11T15:56:13","slug":"acceptance-testing-with-fitnesse-multiplicities-and-comparisons","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/acceptance-testing-with-fitnesse-multiplicities-and-comparisons\/","title":{"rendered":"Acceptance Testing with FitNesse: Multiplicities and Comparisons"},"content":{"rendered":"<div id=\"pretty\">\n<h2>Contents<\/h2>\n<ul>\n<li><a href=\"#heading363998908\">Using FitNesse, Part 6<\/a><\/li>\n<li><a href=\"#heading363998909\">Dealing with Multiplicities<\/a>\n<ul>\n<li><a href=\"#heading363998910\">Combine Multiple Method Calls in One Test Table<\/a><\/li>\n<li><a href=\"#heading363998911\">Combine Multiple Database Queries in One Test Table<\/a><\/li>\n<li><a href=\"#heading363998912\">Work With Multiple Outputs<\/a><\/li>\n<li><a href=\"#heading363998913\">Work With Multiple Output Rows<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#heading363998914\">Comparisons<\/a>\n<ul>\n<li><a href=\"#heading363998915\">Validate Approximate Values<\/a><\/li>\n<li><a href=\"#heading363998916\">Be Aware of the Precision of Your Database Values<\/a><\/li>\n<li><a href=\"#heading363998917\">Watch Out For Whitespace<\/a><\/li>\n<li><a href=\"#heading363998918\">Know What Comprises Your Database Key<\/a><\/li>\n<\/ul>\n<li><a href=\"#heading363998919\">More to Come&#8230;<\/a><\/li>\n<p><b> FitNesse<\/b> is a wiki-based framework for writing <a href=\"http:\/\/en.wikipedia.org\/wiki\/Acceptance_testing\">acceptance tests<\/a> for software systems. If you are not familiar with FitNesse, Part 1 of this series walks through a complete .NET example from writing the test in your browser to writing the C# code-behind. While FitNesse provides a rather nifty and user-friendly way to write acceptance tests in general, in practice there are plenty of quirks and glitches to watch out for. This and the subsequent parts of this series provide &#8220;tips from the trenches&#8221;, i.e. an accumulation of tips collected from intensive use of FitNesse on a daily basis to alleviate or avoid many of those pain points. <\/p>\n<p>Here is your roadmap to the series, showing where you are right now: <\/p>\n<table class=\"series-articles table--bare\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse,-the-overview\/\">Part 1: FitNesse Introduction  and Walkthrough<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>P<a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse-documentation-and-infrastructure\/\">art 2: Documentation and Infrastructure<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse--naming-and-layout\/\">Part 3: Naming and Layout<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse-debugging,-control-flow,-and-tracing\/\">Part 4: Debugging, Control Flow, and Tracing<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse-symbols,-variables-and-code-behind-styles\/\">Part 5: Symbols, Variables, and Code-Behind Style<\/a><\/p>\n<\/td>\n<\/tr>\n<tr class=\"series-articles--active\">\n<td valign=\"top\">\n<p><span class=\"icon--chevron-right\"><\/span><\/p>\n<\/td>\n<td valign=\"top\">\n<p>Part 6: Multiplicities and Comparisons<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse-database-fixtures,-project-overview\/\">Part 7: Database Fixtures, Project Overview<\/a><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"note\">\n<p class=\"note\">Most sections in this article have references with actual hyperlinks to the FitNesse, fitSharp, or DbFit reference material. Some also have references to the sample test suite accompanying this series of articles, e.g. <b>CleanCode.ConceptNotes.LayoutShowingEmbeddedNewlines<\/b>. That path refers to a page on your FitNesse server. Thus if you are running on port 8080 on your local machine, the full URL to visit that page would be:<br \/>\n            <br \/><code>http:\/\/localhost:8080\/CleanCode.ConceptNotes.LayoutShowingEmbeddedNewlines<\/code><\/p>\n<\/p><\/div>\n<h2 id=\"heading363998909\">Dealing with Multiplicities<\/h2>\n<h3 id=\"heading363998910\">Combine Multiple Method Calls in One Test Table<\/h3>\n<p>Do not use separate test tables for similar calls to the same method (i.e. calls that have the same signature). Compact your test vertically by combining calls to the same method in a <i>single<\/i> test table. This also adds additional structure to your tests by making it obvious that multiple calls to a method are, in fact, calling the same method.<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>With  Separate Tables<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Combined into a Single Table<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Create Collection|<br \/>\n                    |Add Value   |Size?|<br \/>\n                    |black,blue  |2    |<\/p>\n<p>                    !|Create Collection|<br \/>\n                    |Add Value   |Size?|<br \/>\n                    |green       |1    |<br \/>\n                    <\/code>\n                <\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Create Collection|<br \/>\n                    |Add Values  |Size?|<br \/>\n                    |black,blue  |2    |<br \/>\n                    |green       |1    |<br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When you have multiple calls to a single method, each is completely independent of the others and the order of execution is deterministic (i.e. the operations are performed sequentially in the order you specify). In the example shown, the <span class=\"Code-inline\">Concat<\/span> method simply concatenates the two values given with the specified separator. The output of the first call-stored in the <span class=\"Code-inline\">FileName<\/span> symbol-is used in both the second and third calls (highlighted in red to make this more obvious).<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Concat                                                     |<br \/>\n                    |Separator|Value1         |Value2             |Result?       |<br \/>\n                    |_        |&lt;&lt;ProgramNumber|${SEED}_badfile.xls|<b>&gt;&gt;FileName<\/b>    |<br \/>\n                    |\\        |&lt;&lt;PaymentFolder|<b>&lt;&lt;FileName<\/b>         |&gt;&gt;TestFile    |<br \/>\n                    |%        |out of range   |<b>&lt;&lt;FileName <\/b>        |&gt;&gt;ErrMessage  |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 id=\"heading363998911\">Combine Multiple Database Queries in One Test Table<\/h3>\n<p>A similar concept applies to combining multiple database query results. Rather than invoke the same query with a different parameter, make use of SQL language constructs. In this example use <span class=\"Code-inline\">IN<\/span> instead of a single <span class=\"Code-inline\">EQUALS<\/span> comparison in the <span class=\"Code-inline\">WHERE<\/span> clause. (Remember that within queries, <span class=\"Code-inline\">@name<\/span> is the notation to reference a symbol.)<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b> <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>With  Separate Tables<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Combined into a Single Table<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Query|select SUM ...WHERE id=\"B\" style=\"mso-bidi-font-weight: normal\"&gt;@Name1|<br \/>\n                    |Sum             |Count                |<br \/>\n                    |${SumOfDetails1}|${CountOfDetails1}   |<\/p>\n<p>                    !|Query|select SUM ...WHERE id=\"B\" style=\"mso-bidi-font-weight: normal\"&gt;@Name2|<br \/>\n                    |Sum             |Count                |<br \/>\n                    |${SumOfDetails2}|${CountOfDetails2}   |<\/p>\n<p>                    !|Query|select SUM ...WHERE id=\"B\" style=\"mso-bidi-font-weight: normal\"&gt;@Name3|<br \/>\n                    |Sum             |Count                |<br \/>\n                    |${SumOfDetails3}|${CountOfDetails3}   |<br \/>\n                    <\/code>\n                <\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Query|select SUM ...!-<br \/>\n                    -!WHERE name in (<b>@Name1,@Name2,@Name3<\/b>)|<br \/>\n                    |Sum              |Count              |<br \/>\n                    |${SumOfDetails1} |${CountOfDetails1} |<br \/>\n                    |${SumOfDetails2} |${CountOfDetails2} |<br \/>\n                    |${SumOfDetails3} |${CountOfDetails3} |<br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>And, just as in the last section with multiple <i>method<\/i> calls, with multiple <i>query<\/i> calls (where you have some one or more expected fields to identify a row) you can order the rows how you like and then use results from earlier rows in later rows. Here the query stores the <span class=\"Code-inline\">BatchNum<\/span> corresponding to an <span class=\"Code-inline\">Id<\/span> of 1 and verifies the same <span class=\"Code-inline\">BatchNum<\/span> applies to an <span class=\"Code-inline\">Id<\/span> of 2.<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|query|select Id, BatchNum from ...|<br \/>\n                    |Id    |BatchNum?                   |<br \/>\n                    |1     |&gt;&gt;Bat1                      |<br \/>\n                    |2     |&lt;&lt;Bat1                      |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 id=\"heading363998912\">Work With Multiple Outputs<\/h3>\n<p>All the examples thus far have dealt with test tables that take one or more inputs but produce exactly one output. Part of the reason for that is that it is natural to imagine an equivalence relationship between a test table and a method, and a method returns just one output. That is fine for many cases. But you are not limited to that mental model. Rather, consider that you are communicating with a class rather a method, and you have much more flexibility.<\/p>\n<p>For each set of inputs, then, you can get not just one output but one <i>row<\/i> of output, though this output &#8220;row&#8221; is adjacent to-rather than under-the input &#8220;row&#8221;. Consider a simple <span class=\"Code-inline\">Echo<\/span> fixture class, shown below. I have contrived it for purposes of discussion to return up to two outputs, <span class=\"Code-inline\">Result<\/span> and <span class=\"Code-inline\">ResultTimesTwo<\/span>, for a given input. This is done by deriving from the <span class=\"Code-inline\">ColumnFixture<\/span> class, likely the most common pattern to use in FitNesse.<\/p>\n<pre class=\"lang:c# theme:vs2012\">using fit;\r\nnamespace CleanCodeFixtures.Common\r\n{\r\n    public class EchoFixture : ColumnFixture\r\n    {\r\n        public object Value;\r\n \r\n        public string Result()\r\n        {\r\n            return Value.ToString();\r\n        }\r\n \r\n        public string ResultTimesTwo()\r\n        {\r\n            return Value.ToString() + \",\" + Value.ToString();\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Here is an example test table that does two separate operations. It first provides an input of &#8220;hello&#8221; and gets the results &#8220;hello&#8221; and &#8220;hello,hello&#8221;. The second invocation does the same for the string &#8220;world&#8221;.<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Echo                          |<br \/>\n                    |Value|Result?|Result Times Two?|<br \/>\n                    |hello|hello  |hello,hello      |<br \/>\n                    |world|world  |world,world      |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF8.jpg\" alt=\"1936-imgF8.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\" colspan=\"3\">\n<p>Echo<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Value<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Result?<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Result Times Two?<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>hello<\/p>\n<\/td>\n<td valign=\"top\">\n<p>hello<\/p>\n<\/td>\n<td valign=\"top\">\n<p>hello,hello<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>world<\/p>\n<\/td>\n<td valign=\"top\">\n<p>world<\/p>\n<\/td>\n<td valign=\"top\">\n<p>world,world<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p> For each input, you need either a public field (as shown here) or a publicly settable property. For each output, you need either a public field, a publicly readable property, or a public method. <\/p>\n<p>Here is one more example with one input and four outputs, which you may remember from Part 1 of this series (where you can also find the code-behind for the class):<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Top Word                                                    |<br \/>\n                    |Input       |Word?|Occurrences?|Different Words?|Total Words?|<br \/>\n                    |${InputText}|black|3           |4               |7           |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p> Reference: <a href=\"http:\/\/fitnesse.org\/FitNesse.UserGuide.FixtureGallery.BasicFitFixtures.ColumnFixture\">Column Fixture<\/a><\/p>\n<h3 id=\"heading363998913\">Work With Multiple Output Rows<\/h3>\n<p>The commonly used <span class=\"Code-inline\">ColumnFixture<\/span> class, as shown previously, allows you to get multiple return values (i.e. different columns) but returns only a single row per set of inputs. <span class=\"Code-inline\">RowFixture<\/span>, on the other hand, may be used to return any number of associated rows. In this example, the <span class=\"Code-inline\">InvoiceDetailsFixture<\/span> class inherits from <span class=\"Code-inline\">RowFixture<\/span> rather than <span class=\"Code-inline\">ColumnFixture<\/span>, taking no inputs and returning a series of rows each containing two outputs.<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Invoice Details           |<br \/>\n                    |Invoice Number|Client Name |<br \/>\n                    |A123          |&gt;&gt;Smith     |<br \/>\n                    |B456          |Wilma Jones |<br \/>\n                    |C923          |&lt;&lt;Smith     |<br \/>\n                    |D111          |Casper      |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF8.jpg\" alt=\"1936-imgF8.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\" colspan=\"2\">\n<p>Invoice Details<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Invoice Number<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Client Name<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>A123<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&gt;&gt;Smith <b>Fred Smith<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>B456<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Wilma Jones<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>C923<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&lt;&lt; Smith <b>Fred Smith<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>D111<b> <i>missing<\/i><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>Casper<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>F987<b><i> surplus<\/i><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>George Bailey<\/b><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When executed this test validates all the rows specified; here the D111 row was not returned so is marked as <i>missing<\/i>, while one additional row (F987) was not expected, so is marked as <i>surplus<\/i>. Also note the use of symbols; one is collected from the first row and used to verify the third row. That is, just like a <span class=\"Code-inline\">ColumnFixture<\/span>, <span class=\"Code-inline\">RowFixture<\/span> results are ordered and you can use values from earlier rows in later rows.<\/p>\n<p>Here is the code-behind for this fixture class:<\/p>\n<pre class=\"lang:c# theme:vs2012\">using System;\r\nusing System.Collections.Generic;\r\nusing fit;\r\n \r\nnamespace CleanCodeFixtures.Demo\r\n{\r\n    public class InvoiceDetailsFixture : RowFixture\r\n    {\r\n        private readonly List&lt;Invoice&gt; _invoices = new List&lt;Invoice&gt;\r\n            {\r\n                new Invoice {InvoiceNumber = \"A123\", ClientName = \"Fred Smith\"},\r\n                new Invoice {InvoiceNumber = \"B456\", ClientName = \"Wilma Jones\"},\r\n                new Invoice {InvoiceNumber = \"C923\", ClientName = \"Fred Smith\"},\r\n                new Invoice {InvoiceNumber = \"F987\", ClientName = \"George Bailey\"},\r\n            };\r\n        public override Type GetTargetClass() { return typeof(Invoice); }\r\n        public override object[] Query() { return _invoices.ToArray(); }\r\n    }\r\n \r\n    public class Invoice\r\n    {\r\n        public string InvoiceNumber { get; set; }\r\n        public string ClientName { get; set; }\r\n    }\r\n}\r\n<\/pre>\n<p>If you compare the test to the fixture you can see how the fixture returns a series of <span class=\"Code-inline\">Invoice<\/span> objects to the test page. This is done quite simply by overriding the <span class=\"Code-inline\">GetTargetClass<\/span> method to identify the type of the return object, and by overriding the <span class=\"Code-inline\">Query<\/span> method to do whatever you want to generate a list of those objects. Here, the fixture is simply returning a hardcoded list; in practice you will return data generated either in code or retrieved from a database.<\/p>\n<p>Reference: <a href=\"http:\/\/fitnesse.org\/FitNesse.UserGuide.FixtureGallery.BasicFitFixtures.RowFixture\">Row Fixture<\/a>, <a href=\"http:\/\/fitnesse.org\/FitNesse.UserGuide.FixtureGallery.ImportantConcepts.FixtureArguments\">Fixture Arguments<\/a> (FitNesse), <a href=\"http:\/\/fitsharp.github.io\/Fit\/FixtureArguments.html\">Fixture Arguments<\/a> (fitSharp)<\/p>\n<h2 id=\"heading363998914\">Comparisons<\/h2>\n<h3 id=\"heading363998915\">Validate Approximate Values<\/h3>\n<p>Up until now, you have seen test assertions done by filling in a cell with a value that must be matched exactly by the returned data; otherwise, that cell is marked as failing. FitSharp provides a number of cell operators that give you more expressiveness than just an equality check. For example, once you load any of these FitSharp string operators you can do partial string matches as shown. (Note that these <i>cell operators<\/i> are a replacement for the old <i>cell handlers<\/i>.)<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !*&gt; setup<br \/>\n                    !|configuration setup          |<br \/>\n                    |service                       |<br \/>\n                    |add operator|CompareStartsWith|<br \/>\n                    |add operator|CompareEndsWith  |<br \/>\n                    |add operator|CompareSubstring |<br \/>\n                    *!<\/p>\n<p>                    !|Echo         |<br \/>\n                    |Value |Result?|<br \/>\n                    |abcdef|abc..  |<br \/>\n                    |abcdef|..def  |<br \/>\n                    |abcdef|..def..|<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF8.jpg\" alt=\"1936-imgF8.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>&#9658;<i>setup<\/i><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\" colspan=\"2\">\n<p>Echo<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Value<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Result?<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>abcdef<\/p>\n<\/td>\n<td valign=\"top\">\n<p>abc..<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>abcdef<\/p>\n<\/td>\n<td valign=\"top\">\n<p>..def<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>abcdef<\/p>\n<\/td>\n<td valign=\"top\">\n<p>..def..<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgFC.gif\" alt=\"1936-imgFC.gif\" \/><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>&#9658;<i>setup<\/i><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\" colspan=\"2\">\n<p><i>Echo<\/i><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Value<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Result?<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>abcdef<\/p>\n<\/td>\n<td valign=\"top\">\n<p>abc..<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>abcdef<\/p>\n<\/td>\n<td valign=\"top\">\n<p>..def<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>abcdef<\/p>\n<\/td>\n<td valign=\"top\">\n<p>..def..<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Other operators allow comparing strings with options to ignore white space and\/or case (<span class=\"Code-inline\">CompareString<\/span>), asserting that an integer lies within a given range (<span class=\"Code-inline\">CompareIntegralRange<\/span>) or match a string to a regular expression (<span class=\"Code-inline\">CompareRegEx<\/span>).<\/p>\n<p>References: <a href=\"http:\/\/fitsharp.github.io\/Fit\/CellOperators.html\">fitSharp Cell Operators<\/a>, <a href=\"https:\/\/fitnesse.s3.amazonaws.com\/tdd_net_with_fitnesse.pdf\">Cell Operators for Simpler Comparisons<\/a> (see chapter 15), CleanCode.GeneralUtilityFixtureNotes.EchoFixture<\/p>\n<h3 id=\"heading363998916\">Be Aware of the Precision of Your Database Values<\/h3>\n<p>Take care with specifying precision when you retrieve and compare certain data types from a database. Besides the obvious consideration of floating point numbers there are other key types to consider as well, such as dates and times. This statement&#8230;<\/p>\n<pre>    SELECT CreatedDate FROM...<\/pre>\n<p> &#8230;retrieves the time down to the millisecond-probably not what you want! Rather convert it to a precision relevant to the task at hand. If you only want to validate the date and ignore the time altogether, use something like this (highlighted in red for emphasis):<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|Query|!-SELECT RecordID, WidgetID, Price, Active,<br \/>\n                    CONVERT(char(10),DateValidFrom,101) [DateValidFrom],<br \/>\n                    DateValidFrom as [DateValidFrom_Original]<br \/>\n                    -!FROM dbo.${MyTable}!-<br \/>\n                    -!WHERE Price &gt; @TargetPrice|<br \/>\n                    |RecordID |WidgetID|Price|Active|DateValidFrom|DateValidFrom_Original|<br \/>\n                    |&lt;&lt;record1|25      |1010 |      |01\/01\/2013   |                      |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<tr>\n<td>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF8.jpg\" alt=\"1936-imgF8.jpg\" \/><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>Query<\/p>\n<\/td>\n<td valign=\"top\" colspan=\"5\">\n                                <code><br \/>\n                                    SELECT RecordID, WidgetID, Price, Active,<br \/>\n                                    <b>CONVERT(char(10),DateValidFrom,101) [DateValidFrom],<\/b><br \/>\n                                    DateValidFrom as [DateValidFrom_Original]<br \/>\n                                    FROM dbo.MyWidgets_tmp<br \/>\n                                    WHERE Price &gt; @TargetPrice<br \/>\n                                    <\/code>\n                                <\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>RecordID <\/p>\n<\/td>\n<td valign=\"top\">\n<p>WidgetID <\/p>\n<\/td>\n<td valign=\"top\">\n<p>Price <\/p>\n<\/td>\n<td valign=\"top\">\n<p>Active <\/p>\n<\/td>\n<td valign=\"top\">\n<p>DateValidFrom <\/p>\n<\/td>\n<td valign=\"top\">\n<p>DateValidFrom_Original<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>&lt;&lt;record1 <\/p>\n<\/td>\n<td valign=\"top\">\n<p>25 <\/p>\n<\/td>\n<td valign=\"top\">\n<p>1010 <\/p>\n<\/td>\n<td valign=\"top\">\n<\/td>\n<td valign=\"top\">\n<p>01\/01\/2013 <\/p>\n<\/td>\n<td valign=\"top\">\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>&lt;&lt;record2 <\/p>\n<\/td>\n<td valign=\"top\">\n<p>12 <\/p>\n<\/td>\n<td valign=\"top\">\n<p>1011 <\/p>\n<\/td>\n<td valign=\"top\">\n<\/td>\n<td valign=\"top\">\n<p>05\/01\/2013 <\/p>\n<\/td>\n<td valign=\"top\">\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgFC.gif\" alt=\"1936-imgFC.gif\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><i>Query<\/i><\/p>\n<\/td>\n<td valign=\"top\" colspan=\"5\">\n                                <code><br \/>\n                                    SELECT RecordID, WidgetID, Price, Active,<br \/>\n                                    CONVERT(char(10),DateValidFrom,101) [DateValidFrom],<br \/>\n                                    DateValidFrom as [DateValidFrom_Original]<br \/>\n                                    FROM dbo.MyWidgets_tmp<br \/>\n                                    WHERE Price &gt; @TargetPrice<br \/>\n                                    <\/code>\n                                <\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>RecordID <\/p>\n<\/td>\n<td valign=\"top\">\n<p>WidgetID <\/p>\n<\/td>\n<td valign=\"top\">\n<p>Price <\/p>\n<\/td>\n<td valign=\"top\">\n<p>Active <\/p>\n<\/td>\n<td valign=\"top\">\n<p>DateValidFrom <\/p>\n<\/td>\n<td valign=\"top\">\n<p>DateValidFrom_Original<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>&lt;&lt;record1  <b>1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>25 <\/p>\n<\/td>\n<td valign=\"top\">\n<p>1010 <\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>null<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>01\/01\/2013 <\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>01\/01\/2013 10:21:03<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>&lt;&lt;record2  <b>2<\/b> <\/p>\n<\/td>\n<td valign=\"top\">\n<p>12 <\/p>\n<\/td>\n<td valign=\"top\">\n<p>1011 <\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>null<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>05\/01\/2013 <\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>05\/01\/2013 08:15:00<\/b><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>I often include the complete, original value as well for my own benefit, not for validation purposes, as the example illustrates.<\/p>\n<p>References: <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms187928.aspx\">TSQL Cast and Convert<\/a>, CleanCode.DataBaseNotes.CrudOperations<\/p>\n<h3 id=\"heading363998917\">Watch Out For Whitespace<\/h3>\n<p>Watch out for whitespace when you retrieve and compare strings from a database. Unless you know a particular field is well-behaved with respect to whitespace assume it is not-<i>program defensively<\/i>. Consider the example below. If the description is a value the user typed in, then the user may have typed &#8220;dry goods&#8221; or &#8221;    dry goods&#8221; or &#8220;dry    goods&#8221; or &#8220;dry goods      &#8220;. <i>Only<\/i> the first one would produce a positive match against invoice 101 on this test table:<\/p>\n<pre>!|query|SELECT InvoiceNumber, Description !-\r\n-!FROM Invoices WHERE CustNum = ${CustNum}|\r\n|InvoiceNumber|Description  |\r\n|101          |dry goods    |\r\n|102          |paraphernalia|\r\n|103          |habadashery  |\r\n<\/pre>\n<p>You might see the problem if the second or third variation had been typed, but you will likely not see it if the last one is typed (the one with trailing spaces). To obfuscate the problem even further, the test table itself <i>ignores trailing spaces in what you enter<\/i>. That is all of these lines are equivalent in a test table:<\/p>\n<pre>|InvoiceNumber|Description  |\r\n|101          |dry goods|\r\n|101          |dry goods  |\r\n|101          |dry goods    |\r\n<\/pre>\n<p> They will match only &#8220;dry goods&#8221; in the database though so if the database actually contains &#8220;dry goods  &#8221; you will think FitNesse is broken because in essence it reports &#8220;dry goods&#8221; does not match &#8220;dry goods&#8221;! The solution is to use the RTRIM function in SQL:<\/p>\n<pre class=\"lang:c# theme:vs2012\">!|query|SELECT InvoiceNumber, RTRIM(Description) as Description!-\r\n-!FROM Invoices WHERE CustNum = ${CustNum}|\r\n|InvoiceNumber|Description  |\r\n|101          |dry goods    |\r\n|102          |paraphernalia|\r\n|103          |habadashery  |\r\n<\/pre>\n<h3 id=\"heading363998918\">Know What Comprises Your Database Key<\/h3>\n<p>In a non-database test table, the presence of a trailing question mark or trailing parentheses attached to a column name is what distinguishes inputs from outputs. In a database test table, the presence of a trailing question mark indicates a field that is <i>not<\/i> part of the record&#8217;s composite key; omitting the question mark includes it in the composite key.  This partial key matching allows non-key values to be compared yet not used in identifying a row. Here is the distinction. In this first example, columns C and D are marked as non-key fields but they are validated because we put values in the table cells.<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                    <code><br \/>\n                    !|query|SELECT 1 ColA, 2 ColB, 3 ColC, 4 ColD!-<br \/>\n                    -! UNION SELECT 21, 22, 23, 24!-<br \/>\n                    -! UNION SELECT 31, 32, 33, 34|<br \/>\n                    |ColA|ColB|ColC?|ColD?|<br \/>\n                    |1   |2   |3    |4    |<br \/>\n                    |21  |22  |21   |21   |<br \/>\n                    |31  |32  |33   |34   |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgFC.gif\" alt=\"1936-imgFC.gif\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>query<\/p>\n<\/td>\n<td valign=\"top\" colspan=\"3\">\n                                <code><br \/>\n                                    SELECT 1 ColA, 2 ColB, 3 ColC, 4 ColD<br \/>\n                                    UNION SELECT 21, 22, 23, 24<br \/>\n                                    UNION SELECT 31, 32, 33, 34<br \/>\n                                    <\/code>\n                                <\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>ColA <\/p>\n<\/td>\n<td valign=\"top\">\n<p>ColB<\/p>\n<\/td>\n<td valign=\"top\">\n<p>ColC?<\/p>\n<\/td>\n<td valign=\"top\">\n<p>ColD?<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>3<\/p>\n<\/td>\n<td valign=\"top\">\n<p>4<b><i><\/i><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>21<\/p>\n<\/td>\n<td valign=\"top\">\n<p>22<\/p>\n<\/td>\n<td valign=\"top\">\n                                <code><br \/>\n                                    21 <span class=\"fitlabel\"><i>expected<\/i><i><\/i><\/span><br \/>\n                                    <span class=\"fitlabel\">---------------<\/span><br \/>\n                                    23 <span class=\"fitlabel\"><i>actual<\/i><i><\/i><\/span><i><\/i><br \/>\n                                    <\/code>\n                                <\/td>\n<td valign=\"top\">\n                                <code><br \/>\n                                    21 <span class=\"fitlabel\"><i>expected<\/i><i><\/i><\/span><br \/>\n                                    <span class=\"fitlabel\">---------------<\/span><br \/>\n                                    24 <span class=\"fitlabel\"><i>actual<\/i><i><\/i><\/span><b><i><\/i><\/b><br \/>\n                                    <\/code>\n                                <\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>31<\/p>\n<\/td>\n<td valign=\"top\">\n<p>32<\/p>\n<\/td>\n<td valign=\"top\">\n<p>33<\/p>\n<\/td>\n<td valign=\"top\">\n<p>34<b><i><\/i><\/b><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If you omit question marks from the non-key columns they cause rows to be rejected completely-at least this is the way it worked in a prior release (for .NET) and still does (for Java). I wanted to include this section precisely because of this current difference between Java and .NET implementations-something to be aware of.<\/p>\n<table class=\"MsoTableLightGridAccent3 process-table\">\n<tbody>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgF5.jpg\" alt=\"1936-imgF5.jpg\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n                <code><br \/>\n                    !|query|SELECT 1 ColA, 2 ColB, 3 ColC, 4 ColD!-<br \/>\n                    -! UNION SELECT 21, 22, 23, 24!-<br \/>\n                    -! UNION SELECT 31, 32, 33, 34|<br \/>\n                    |ColA|ColB|ColC|ColD|<br \/>\n                    |1   |2   |3   |4   |<br \/>\n                    |21  |22  |21  |21  |<br \/>\n                    |31  |32  |33  |34  |<b><\/b><br \/>\n                    <\/code>\n                <\/td>\n<\/tr>\n<tr>\n<td>\n<p><b><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1936-imgFC.gif\" alt=\"1936-imgFC.gif\" \/><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>query<\/p>\n<\/td>\n<td valign=\"top\" colspan=\"3\">\n                                <code><br \/>\n                                    SELECT 1 ColA, 2 ColB, 3 ColC, 4 ColD<br \/>\n                                    UNION SELECT 21, 22, 23, 24<br \/>\n                                    UNION SELECT 31, 32, 33, 34<br \/>\n                                    <\/code>\n                                <\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>ColA <\/p>\n<\/td>\n<td valign=\"top\">\n<p>ColB<\/p>\n<\/td>\n<td valign=\"top\">\n<p>ColC<\/p>\n<\/td>\n<td valign=\"top\">\n<p>ColD<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>3<\/p>\n<\/td>\n<td valign=\"top\">\n<p>4<b><i><\/i><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>21<span class=\"fitlabel\"><i> missing<\/i><i><\/i><\/span><\/p>\n<\/td>\n<td valign=\"top\">\n<p>22<\/p>\n<\/td>\n<td valign=\"top\">\n<p>21<\/p>\n<\/td>\n<td valign=\"top\">\n<p>21<b><i><\/i><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>31<\/p>\n<\/td>\n<td valign=\"top\">\n<p>32<\/p>\n<\/td>\n<td valign=\"top\">\n<p>33<\/p>\n<\/td>\n<td valign=\"top\">\n<p>34<b><i><\/i><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>21<span class=\"fitlabel\"><i> surplus<\/i><i><\/i><\/span><\/p>\n<\/td>\n<td valign=\"top\">\n<p>22<\/p>\n<\/td>\n<td valign=\"top\">\n<p>23<\/p>\n<\/td>\n<td valign=\"top\">\n<p>24<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>References: CleanCode.DataBaseNotes.SpecifyingFieldsToMatch<\/p>\n<h2 id=\"heading363998919\"><a id=\"heading362788079\">More to Come&#8230;<\/a><\/h2>\n<p>Part 7 provides details on database operations as well as an overview of the fixture library and sample test suite accompanying this series of articles.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style interface that makes it very easy to set up individual tests. Michael Sorens&#8217; sixth article in his series delves into the nuances of multiple inputs vs. multiple outputs, multiple rows vs. multiple columns, as well as things that can trip you up when attempting to validate a value.&hellip;<\/p>\n","protected":false},"author":221868,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4204],"coauthors":[6802],"class_list":["post-1758","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-net-tools"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1758","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221868"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1758"}],"version-history":[{"count":15,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1758\/revisions"}],"predecessor-version":[{"id":72854,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1758\/revisions\/72854"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1758"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1758"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1758"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1758"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}