{"id":1724,"date":"2013-11-15T00:00:00","date_gmt":"2013-11-15T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/acceptance-testing-with-fitnesse-debugging-control-flow-and-tracing\/"},"modified":"2021-05-11T15:56:14","modified_gmt":"2021-05-11T15:56:14","slug":"acceptance-testing-with-fitnesse-debugging-control-flow-and-tracing","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/acceptance-testing-with-fitnesse-debugging-control-flow-and-tracing\/","title":{"rendered":"Acceptance Testing with FitNesse: Debugging, Control Flow, and Tracing"},"content":{"rendered":"<div id=\"pretty\">\n<h2>Contents<\/h2>\n<ul>\n<li><a href=\"#first\">Debugging<\/a>\n<ul>\n<li><a href=\"#second\">Use Diagnostics to Get Up And Running<\/a><\/li>\n<li><a href=\"#third\">Connect Your Tests to Visual Studio&#8217;s Debugger<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#fourth\">Control Flow<\/a>\n<ul>\n<li><a href=\"#fifth\">Pause To Examine Resources<\/a><\/li>\n<li><a href=\"#sixth\">Abort Conditionally<\/a><\/li>\n<li><a href=\"#seventh\">Abort Unconditionally<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#eighth\">Tracing<\/a>\n<ul>\n<li><a href=\"#ninth\">Step 1: Instrument your FitNesse Tests<\/a><\/li>\n<li><a href=\"#tenth\">Step 2: Instrument your CP code<\/a><\/li>\n<li><a href=\"#eleventh\">Step 3: Enable Tracing<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#twelveth\">More to Come <\/a><\/li>\n<\/ul>\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>\u00a0<\/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\u00a0 and Walkthrough<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>\u00a0<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse-documentation-and-infrastructure\/\">Part 2: Documentation and Infrastructure<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>\u00a0<\/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 class=\"series-articles--active\">\n<td valign=\"top\">\n<p>&nbsp;<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Part 4: Debugging, Control Flow, and Tracing<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>\u00a0<\/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>\n<td valign=\"top\">\n<p><b>\u00a0<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><a href=\"https:\/\/www.simple-talk.com\/dotnet\/.net-tools\/acceptance-testing-with-fitnesse-multiplicities-and-comparisons\/\">Part 6: Multiplicities and Comparisons<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>\u00a0<\/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<code>http:\/\/localhost:8080\/CleanCode.ConceptNotes.LayoutShowingEmbeddedNewlines<\/code><\/p>\n<\/div>\n<h2 id=\"first\">Debugging<\/h2>\n<h3 id=\"second\">Use Diagnostics to Get Up And Running<\/h3>\n<p>When you first attempt to configure FitNesse for your .NET environment, you launch the FitNesse server (fitnesse.jar or fitnesse-standalone.jar) and then you go to your browser and attempt to browse (e.g. http:\/\/localhost:<i>port<\/i>). You should typically be able to see that top-level page and other static pages. But when you attempt to run a test you may just see&#8230; nothing. The critical configuration, as described in <b>Differentiating Java from .NET<\/b> in Part 2 of this series, is getting the <code>COMMAND_PATTERN<\/code> and<code> <\/code><code>TEST_RUNNER<\/code> variables set just right. If you do not, you may get an error or you may get a blank page. One diagnostic you can use to see if you are in fact getting to the appropriate test runner is to go the root page (either click on <code>root<\/code> at the bottom of most any page or browse directly to http:\/\/localhost:<i>port<\/i>\/root), edit the page, and modify your definition of <code>TEST_RUNNER<\/code>. <i>You did put that here in the root page, right?<\/i> Change the test runner from <code>Runner.exe <\/code>to <code>RunnerW.exe<\/code>:<\/p>\n<pre>!define TEST_RUNNER {your_path\\RunnerW.exe}<\/pre>\n<p>Save that, return to your test page, and again attempt to run it with the <b>Test<\/b> button. That should pop up a window before it starts running that tells you you have been able to specify a valid path to the <code>TEST_RUNNER<\/code> and have at least made it that far.<\/p>\n<p class=\"caption reference\"><strong>Reference<\/strong>:\u00a0<a href=\"http:\/\/fitsharp.github.io\/Fit\/UsingFitnesse.html\">Using FitNesse<\/a><\/p>\n<h3 id=\"third\">Connect Your Tests to Visual Studio&#8217;s Debugger<\/h3>\n<p>The standard FitNesse documentation walks you through a couple simple examples-equivalent to &#8220;Hello, World&#8221; in FitNesse dialog-so I do not need to repeat that here.\u00a0 So let us say you have some basic tests working and you want to debug your C# test fixture code with Visual Studio&#8217;s debugger. There are actually several ways to do that, presented here in order of <i>decreasing<\/i> cumbersomeness:<\/p>\n<h4 id=\"fourth\"><b>Method 1:<\/b><\/h4>\n<div class=\"indented\">\n<p>This might sound familiar. Go to your root page and redefine <code>TEST_RUNNER<\/code> from <code>Runner.exe<\/code> to <code>RunnerW.exe<\/code>. Then, when you launch a test, FitNesse presents a pop-up dialog, effectively pausing the run, giving you time to go over to Visual Studio and attach a debugger to the process. This certainly works, but it involves a lot of clicks to do the job.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1898-img54.jpg\" alt=\"1898-img54.jpg\" \/><\/p>\n<p>This method applies globally to any test or suite you choose to run.<\/p>\n<\/div>\n<p><b>Method 2:<\/b><\/p>\n<div class=\"indented\">\n<p>\u00a0FitSharp provides a simple fixture for invoking the debugger:<\/p>\n<pre>!|debug|<\/pre>\n<p>When this table is executed, you receive a prompt to start or reuse a Visual Studio instance. You land in Visual Studio, with execution stopped at the definition of this fixture code. From there you can set breakpoints and step into your own code. To disable debugging here, you need to edit your test and either comment out the above table or delete it.<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1898-imgA.jpg\" alt=\"1898-imgA.jpg\" \/><\/p>\n<p>This method may apply to a specific test (if you put the test table in a specific test), multiple tests (put it in a suite setup page or multiple test pages), or globally (put it in a top-level setup or suite setup page that is inherited by all tests).<\/p>\n<\/div>\n<p><b>Method 3:<\/b><\/p>\n<div class=\"indented\">\n<p>My CleanCode fixture library (included in the attached project) allows you to enable or disable debugging with an external switch meaning that, using Windows shortcuts, you can enable or disable debugging with a single click or single key combination of your choosing. Here is how you do it.<\/p>\n<p>Instrument one or more tests (or setup page) with the <code>Debugger<\/code> test table below.<\/p>\n<p>That will change nothing about your test execution because debugging is disabled by default. Included in the library are two simple command files-<code>EnableDebug.cmd<\/code> and <code>DisableDebug.cmd<\/code>-which provide the external trigger. Run <code>EnableDebug.cmd<\/code> to enable debugging then try executing your test again. This time, when that test table is hit, you get the familiar prompt to start or reuse a Visual Studio instance for debugging. You land in Visual Studio, with execution stopped at the definition of this fixture code. From there you can step into and set breakpoints in your own code.<\/p>\n<pre>!|Debugger\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |\r\n|LaunchWithExternalTrigger()|\r\n|\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<p>Set up Windows shortcuts to the two command files and\/or use a keyboard accelerator utility (e.g. SlimKeys) so you could enable\/disable debugging with a double click or with a key combination.<\/p>\n<p>You can use this fixture in a couple ways. Put it in your top-level, inherited <code>SetUp<\/code> page. Then essentially every test will go to the debugger at startup. Alternately, sprinkle it at any one or more points in a given test where you wish to debug. If you just want to pause, though, the <code>Continuation<\/code> fixture provides a <code>Pause<\/code> method that is even easier to use, discussed in the next section.<\/p>\n<\/div>\n<p><b>Method 4: <\/b><\/p>\n<div class=\"indented\">\n<p>You can compile into your application code a different debug mechanism that you can also enable or disable at will without recompiling. Add this line in your fixture code where you want to pause to allow time to attach a debugger:<\/p>\n<pre>DebuggerWait.WaitForDebuggerIfFlagged();<\/pre>\n<p>By default it will not wait at all, i.e. debugging will be disabled. To enable it just create a file named <i>application<\/i>.WAIT (e.g. <code>myprog.exe.WAIT<\/code>) in the same directory as your executable. Then when the above line is reached, the program sleeps, giving you time to attach the debugger. To resume execution simply delete the WAIT file.<\/p>\n<\/div>\n<p><b>Summary of the techniques to connect to a debugger<\/b><\/p>\n<table class=\"MsoTableLightGridAccent3\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>Method<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Applies<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Enabling\/Disabling<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Attach debugger<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>Runner\/RunnerW<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>globally<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Traverse to root and edit each time<\/p>\n<\/td>\n<td valign=\"top\">\n<p>manual<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>FitSharp debug fixture<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>globally or locally<\/p>\n<\/td>\n<td valign=\"top\">\n<p>edit each time<\/p>\n<\/td>\n<td valign=\"top\">\n<p>automatic<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>CleanCode debugger fixture<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>globally or locally<\/p>\n<\/td>\n<td valign=\"top\">\n<p>edit just once; then use external trigger<\/p>\n<\/td>\n<td valign=\"top\">\n<p>automatic<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>CleanCode DebuggerWait<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>locally<\/p>\n<\/td>\n<td valign=\"top\">\n<p>edit just once; then use external trigger<\/p>\n<\/td>\n<td valign=\"top\">\n<p>manual<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"caption reference\">Reference: <a href=\"http:\/\/fitsharp.github.io\/Fit\/DebugFixture.html\">debug fixture<\/a><\/p>\n<h2>Control Flow<\/h2>\n<p>These elements that affect control flow should usually only be used on a temporary basis as you are working out the issues with your test code because they change the workflow of the tests.<\/p>\n<h3 id=\"fifth\">Pause To Examine Resources<\/h3>\n<p>If you want to pause a test to examine some external resource manually, use the <code>Pause<\/code> method in the <code>Continuation<\/code> fixture.<\/p>\n<table class=\"MsoTableLightGridAccent3\">\n<tbody>\n<tr>\n<td>\n<p><b>SOURCE<\/b><\/p>\n<\/td>\n<td valign=\"top\"><code><code><\/code><\/code><\/p>\n<p>!|Continuation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|Pause\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0|<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|Review data in DB if desired&#8230;|<b><\/b><\/p>\n<p><code><br \/>\n                    <\/code><\/td>\n<\/tr>\n<tr>\n<td>\n<p><b>OUTPUT<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>Continuation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Pause<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Review data in DB if desired&#8230;<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p><b>RUN<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1898-img13.jpg\" alt=\"1898-img13.jpg\" \/><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When the test gets to that test table, it pops up the dialog box shown. You can click <code>Continue <\/code>or <code>Abort<\/code> as appropriate after you have examined your external resource. Because pressing <code>Return<\/code> will also activate the button having focus, you can use the <code>Lock<\/code> button to prevent that from inadvertently happening as you switch between the many windows typical of a developer, assured that the <code>Pause<\/code> will not continue or abort until you are explicitly telling it to do so.<\/p>\n<p class=\"caption reference\">Reference: CleanCode.ControlFlow.PausingTestsToExamineExternalResources<\/p>\n<h3 id=\"sixth\">Abort Conditionally<\/h3>\n<p><i>Caution: Use aborts only during test development. Remove them promptly and never commit files to source control containing aborts. An abort, when triggered, halts the entire FitNesse run, not just a single test!<\/i><\/p>\n<p>The <code>AbortIfValue<\/code> method is a conditional abort; it will only abort if the object has a value; otherwise execution continues seamlessly. You may use a <i>symbol<\/i>, <i>variable<\/i> or a <i>SQL query<\/i> (of course, you need to setup your database connection in advance if using the latter). In this example, if any rows are returned, testing is aborted.<\/p>\n<pre>!|Abort\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0|\r\n|AbortIfValue\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0|\r\n|select * from Prices where ProgramNumber like 'A%'|<\/pre>\n<p>Here is an example using a <i>symbol<\/i> (described in a later section). The test will abort only if the symbol is defined (i.e. it has a value) and it is not <code>null<\/code>:<\/p>\n<pre>!|Abort\u00a0\u00a0\u00a0\u00a0\u00a0 |\r\n|AbortIfValue|\r\n|&lt;&lt;foobar\u00a0\u00a0\u00a0 |<\/pre>\n<p>Similarly you can use the <code>AbortIfNoValue<\/code> method to abort on the opposite condition.<\/p>\n<p class=\"caption reference\">Reference: CleanCode.ControlFlow.AbortTests<\/p>\n<h3 id=\"seventh\">Abort Unconditionally<\/h3>\n<div class=\"note\">\n<p class=\"note\"><strong>Caution:<\/strong> Use aborts only during test development. Remove them promptly and never commit files to source control containing aborts. An abort, when triggered, halts the entire FitNesse run, not just a single test!<\/p>\n<\/div>\n<p>In the event that you want to temporarily stop your test unconditionally, you can use this table to abort and return the exit code you specify.<\/p>\n<pre>!|Abort\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 |\r\n|ExitCode|Abort|\r\n|23\u00a0\u00a0\u00a0\u00a0\u00a0 |\u00a0\u00a0\u00a0\u00a0 |<\/pre>\n<p class=\"caption reference\">Reference: CleanCode.ControlFlow.AbortTests<\/p>\n<h2 id=\"eighth\">Tracing<\/h2>\n<p>Besides being able to debug sometimes it is useful just to be able to have a permanent trace log. My fixture library includes a tracing facility wherein you may instrument your fixture code or instrument your test pages, or both, depending on your needs.<\/p>\n<h3 id=\"ninth\">Step 1: Instrument your FitNesse Tests<\/h3>\n<p>The goal here is to bracket each suite in a BEGIN&#8230;END block and each individual test page in a BEGIN&#8230;END block. There are a variety of brute force ways to do that but the approach detailed in this table provides an almost maintenance-free technique.<\/p>\n<table class=\"MsoTableLightGridAccent3\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>Put here&#8230;<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>This table&#8230;<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>In each SetUp page <\/b><\/p>\n<\/td>\n<td valign=\"top\"><code><code><\/code><\/code><\/p>\n<p>|CleanCodeFixtures.Common.Diagnostic|<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|Begin |<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|${PAGE_NAME} |<\/p>\n<p><code><br \/>\n                    <\/code><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>In each TearDown page <\/b><\/p>\n<\/td>\n<td valign=\"top\"><code><code><\/code><\/code><\/p>\n<p>|CleanCodeFixtures.Common.Diagnostic|<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|End |<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|${PAGE_NAME} |<\/p>\n<p><code><br \/>\n                    <\/code><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>In each SuiteSetUp page <\/b><\/p>\n<\/td>\n<td valign=\"top\"><code><code><\/code><\/code><\/p>\n<p>|CleanCodeFixtures.Common.Diagnostic|<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|Begin |<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|${PAGE_PATH}.${PAGE_NAME} |<\/p>\n<p><code><br \/>\n                    <\/code><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>In each SuiteTearDown page <\/b><\/p>\n<\/td>\n<td valign=\"top\"><code><code><\/code><\/code><\/p>\n<p>|CleanCodeFixtures.Common.Diagnostic|<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|End |<\/p>\n<p><code><code><\/code><\/code><\/p>\n<p>|${PAGE_PATH}.${PAGE_NAME} |<i><\/i><\/p>\n<p><code><br \/>\n                    <\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note that if you only have a top-level <code>SetUp<\/code> or <code>TearDown<\/code> page, you only need the <code>SetUp<\/code> or <code>TearDown<\/code> table there; inheritance will take care of the rest.<\/p>\n<p>For <code>SuiteSetUp<\/code> or <code>SuiteTearDown<\/code> pages, however, you need one explicitly for each suite, even if it is just for this markup. Inherited ones will <i>not<\/i> show the proper page paths! Note that you can reduce code duplication by putting this markup and anything else you need for your <code>SuiteSetUp<\/code> into a common file (I have a <code>SuiteSetUpCommon<\/code> at the top-level of my tree) then the entire contents of every <code>SuiteSetUp<\/code> file is this exact, single line:<\/p>\n<pre>!include -setup .CleanCode.SuiteSetUpCommon<\/pre>\n<p class=\"caption reference\">References: CleanCode.DataBaseNotes.SpecifyingFieldsToMatch<\/p>\n<h3 id=\"tenth\">Step 2: Instrument your\u00a0 C# code<\/h3>\n<p>Each non-trivial fixture method should be bracketed with <code>Diagnostic.Enter()<\/code> and <code>Diagnostic.Leave()<\/code>. It is important that these always balance so you need to pay careful attention to multiple exit points and particularly exception handling. If you have multiple exit points, just put a <code>Diagnostic.Leave()<\/code> immediately before each of them. If your method may throw exceptions but you want them to bubble up, you should add an explicit <code>try-catch-finally<\/code> using something like this template to both balance the <code>Enter<\/code>\/<code>Leave<\/code> <i>and<\/i> to report the exception:<\/p>\n<pre class=\"lang:c# theme:vs2012\">public bool Ok()\r\n{\r\n\u00a0\u00a0\u00a0 string exitMessage;\r\n\u00a0\r\n\u00a0\u00a0\u00a0 Diagnostic.Enter();\r\n\u00a0\u00a0\u00a0 try\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 . . .\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 catch (Exception ex)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 exitMessage = ex.Message;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 throw;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 finally\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Diagnostic.Leave(exitMessage);\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 return true;\r\n}\r\n<\/pre>\n<p class=\"caption references\">References: WordFrequency.cs<\/p>\n<h3 id=\"eleventh\">Step 3: Enable Tracing<\/h3>\n<p>In your config file set the <code>FitnesseLogging<\/code> parameter to true. Here you may also change the target logging file (<code>FitnesseLogFileName<\/code>) if desired&#8211;the default is log\\fitnesse.log. Note that you may use an absolute or a relative path; a relative path is relative to the location of the FitNesse JAR file. Also, the directory must exist; if it does not no logging occurs and no error is reported.<\/p>\n<p>Here is a sample trace from running the entire test suite in the accompanying project. This shows tracing of both test pages and fixture code. Test pages show the results upon exit (number right, number wrong, etc.). You can see how it nicely indents based on suites and, for just the one C# file instrumented (WordFrequencyDemo), how it further indents right into the C# code.<\/p>\n<pre>07\/25\/13 03:43:42.053 ENTER CleanCode.GeneralUtilityFixtureNotes.SuiteSetUp\r\n07\/25\/13 03:43:42.113\u00a0\u00a0\u00a0\u00a0 ENTER ConcatFixture TEST PAGE\r\n07\/25\/13 03:43:42.240\u00a0\u00a0\u00a0 \u00a0TOOK : 00:00:00.1251302\r\n07\/25\/13 03:43:42.242\u00a0\u00a0\u00a0\u00a0 LEAVE ConcatFixture TEST PAGE :: 17 right, 0 wrong, 0 ignored, 0 exceptions\r\n07\/25\/13 03:43:42.250\u00a0\u00a0\u00a0\u00a0 ENTER ConvertSymbolsFixture TEST PAGE\r\n07\/25\/13 03:43:42.316\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0635422\r\n07\/25\/13 03:43:42.318\u00a0\u00a0\u00a0\u00a0 LEAVE ConvertSymbolsFixture TEST PAGE :: 2 right, 0 wrong, 0 ignored, 0 exceptions\r\n07\/25\/13 03:43:42.322\u00a0\u00a0\u00a0\u00a0 ENTER CountFixture TEST PAGE\r\n07\/25\/13 03:43:42.334\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0097753\r\n07\/25\/13 03:43:42.336\u00a0\u00a0\u00a0\u00a0 LEAVE CountFixture TEST PAGE :: 4 right, 0 wrong, 0 ignored, 0 exceptions\r\n...\r\n07\/25\/13 03:43:43.201 TOOK : 00:00:01.1458121\r\n07\/25\/13 03:43:43.207 LEAVE CleanCode.GeneralUtilityFixtureNotes.SuiteTearDown\r\n07\/25\/13 03:43:43.219 ENTER CleanCode.DataBaseNotes.SuiteSetUp\r\n07\/25\/13 03:43:43.754\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.5083228\r\n07\/25\/13 03:43:43.759\u00a0\u00a0\u00a0\u00a0 LEAVE CrudOperations TEST PAGE :: 26 right, 0 wrong, 0 ignored, 0 exceptions\r\n07\/25\/13 03:43:43.767\u00a0\u00a0\u00a0\u00a0 ENTER HelloDbTest TEST PAGE\r\n07\/25\/13 03:43:43.785\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0136828\r\n07\/25\/13 03:43:43.790\u00a0\u00a0\u00a0\u00a0 LEAVE HelloDbTest TEST PAGE :: 2 right, 0 wrong, 0 ignored, 0 exceptions\r\n07\/25\/13 03:43:43.803\u00a0\u00a0\u00a0\u00a0 ENTER SimpleFetch TEST PAGE\r\n07\/25\/13 03:43:43.883\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0762491\r\n07\/25\/13 03:43:43.887\u00a0\u00a0\u00a0\u00a0 LEAVE SimpleFetch TEST PAGE :: 2 right, 3 wrong, 0 ignored, 0 exceptions\r\n07\/25\/13 03:43:43.895 TOOK : 00:00:00.6696259\r\n07\/25\/13 03:43:43.899 LEAVE CleanCode.DataBaseNotes.SuiteTearDown\r\n07\/25\/13 03:43:43.905 ENTER CleanCode.ConceptNotes.SuiteSetUp\r\n07\/25\/13 03:43:43.916\u00a0\u00a0\u00a0\u00a0 ENTER AccessWindowsEnvironmentVariables TEST PAGE\r\n07\/25\/13 03:43:43.923\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0039126\r\n07\/25\/13 03:43:43.927\u00a0\u00a0\u00a0\u00a0 LEAVE AccessWindowsEnvironmentVariables TEST PAGE :: 2 right, 0 wrong, 0 ignored, 0 exceptions\r\n07\/25\/13 03:43:43.935\u00a0\u00a0\u00a0\u00a0 ENTER AppSettingsTestPage TEST PAGE\r\n07\/25\/13 03:43:43.950\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0117319\r\n07\/25\/13 03:43:43.956\u00a0\u00a0\u00a0\u00a0 LEAVE AppSettingsTestPage TEST PAGE :: 2 right, 0 wrong, 0 ignored, 0 exceptions\r\n...\r\n07\/25\/13 03:43:44.439 TOOK : 00:00:00.5298449\r\n07\/25\/13 03:43:44.443 LEAVE CleanCode.ConceptNotes.SuiteTearDown\r\n\u00a0\r\n07\/25\/13 09:14:41.430\u00a0\u00a0\u00a0\u00a0 ENTER DefineAndUseSymbolsAndVariables TEST PAGE\r\n07\/25\/13 09:14:41.450\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0127083\r\n07\/25\/13 09:14:41.456\u00a0\u00a0\u00a0\u00a0 LEAVE DefineAndUseSymbolsAndVariables TEST PAGE :: Test Case Counts: 6 right, 0 wrong, 0 ignored, 0 exceptions\r\n07\/25\/13 09:14:41.468\u00a0\u00a0\u00a0\u00a0 ENTER WordFrequencyDemo TEST PAGE\r\n07\/25\/13 09:14:41.483\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ENTER WordFrequency.MostFrequentDetails\r\n07\/25\/13 09:14:41.490\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ENTER WordFrequency.GenerateWordFrequencyList\r\n07\/25\/13 09:14:41.499\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ENTER WordFrequency.RawWordList\r\n07\/25\/13 09:14:41.506\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00\r\n07\/25\/13 09:14:41.512\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 LEAVE WordFrequency.RawWordList\r\n07\/25\/13 09:14:41.525\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0273735\r\n07\/25\/13 09:14:41.532\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 LEAVE WordFrequency.GenerateWordFrequencyList\r\n07\/25\/13 09:14:41.561\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0733245\r\n07\/25\/13 09:14:41.566\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 LEAVE WordFrequency.MostFrequentDetails\r\n07\/25\/13 09:14:41.675\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ENTER WordFrequency.GenerateWordFrequencyList\r\n07\/25\/13 09:14:41.680\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ENTER WordFrequency.RawWordList\r\n07\/25\/13 09:14:41.683\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00\r\n07\/25\/13 09:14:41.687\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 LEAVE WordFrequency.RawWordList\r\n07\/25\/13 09:14:41.691\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.0117296\r\n07\/25\/13 09:14:41.695\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 LEAVE WordFrequency.GenerateWordFrequencyList\r\n07\/25\/13 09:14:41.701\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ENTER WordFrequency.RawWordList\r\n07\/25\/13 09:14:41.705\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00\r\n07\/25\/13 09:14:41.708\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 LEAVE WordFrequency.RawWordList\r\n07\/25\/13 09:14:41.712\u00a0\u00a0\u00a0\u00a0 TOOK : 00:00:00.2395055\r\n07\/25\/13 09:14:41.716\u00a0\u00a0\u00a0\u00a0 LEAVE WordFrequencyDemo TEST PAGE :: Test Case Counts: 12 right, 0 wrong, 0 ignored, 0 exceptions\r\n...\r\n<\/pre>\n<p class=\"caption reference\">References: CleanCode.ConceptNotes.InstrumentingYourCodeForTracing, CleanCode.SetUp, CleanCode.SuiteSetUp, CleanCode.SuiteSetUpCommon<\/p>\n<h2 id=\"twelveth\">More to Come&#8230;<\/h2>\n<p>Part 5 continues with everything you ever wanted to know about using symbols and variables effectively, and why those terms are themselves rather problematic!<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>FitNesse is an automated testing tool for software. based on Ward Cunningham&#8217;s Framework for Integrated Test. It is designed particularly for acceptance testing and works with both applications and databases. In part 4,  Michael Sorens shows you how to debug with Visual Studio, manage control flow and enable tracing.&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,4168,4179],"coauthors":[6802],"class_list":["post-1724","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-net-tools","tag-database","tag-source-control"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1724","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=1724"}],"version-history":[{"count":13,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1724\/revisions"}],"predecessor-version":[{"id":84280,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1724\/revisions\/84280"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1724"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1724"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1724"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1724"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}