I promised (myself) that this blog would be more of a notebook; a stream of consciousness describing my own discoveries. I’m not sure if what I’m going over is old hat for the Fitnesse veteran, or if organizing acceptance tests and unit tests in this way is anything of value to anyone other than myself. Although there are tutorials of setting up Fitnesse for .net already out there, I’m putting down my own personal take on the matter as what I found wasn’t exactly what I was looking for all in one place. It’s what I’m involved with at the moment.
This was all sparked when I attended Maykel Suarez’s presentation on Fitnesse in flow mode. I’ve learned a lot from Mike on testing and software in general since I’ve had the privilege of working with him. I was put off from Fitnesse because of some of the misuse and abuse I’ve seen in the wild. My main beef was that folk were using it as a scripting platform. This became a tangled mess of imperative goo, and the textarea provides a poor IDE in my opinion. After attending Mike’s presentation I realized I could use the wiki to document the intention of the test, and that there was already support from like minded people to make this happen.
Checkout the acceptance test I wrote for a hypothetical POS system. Notice, there’s nothing techie in there. In fact, it even looks like user documentation. A sign, that perhaps this is a good acceptance test.
For the Fitnesse guru’s; this particular example could probably have been written more efficiently using a good ol’ fashion ColumnFixture, but I wanted to illustrate the use of plain-text tables and how they can provide a very flexible test structure. You’ll see what the underlying Fitnesse code looks like in a second. I also could have taken the introduction text and put that inside the table to use as the driver for the test.
Getting All the Pieces in Place
- Go get the latest build of Fitnesse. Just download the jar file like it says (and if you don’t have Java installed you’ll need that as well).
- Go get the latest fitsharp library. This provides the runner and the hooks for Fitnesse to execute .net code.
- Create a test project. I use MSTest, but whatever you’re into is cool. I like to name my test project something like GoGoGrocer.Specifications, where GoGoGrocer is the name of the system/application.
- Create a folder to hold your acceptance tests. I name mine AcceptanceTests. Go figure.
- Dump the fitnesse.jar file you downloaded directly into the AcceptanceTests folder.
- Create a little batch file to handle starting up Fitnesse and put these options in it:
java -jar fitnesse.jar -p 8080 -e 0
There are a lot of options to start Fitnesse, and a big help was to realize how useful the Quick Reference section was in the User Guide. The options above start the Fitnesse server on port 8080 (-p) and the turn off version history – you know the wiki thing (-e 0). - Create a bin folder under AcceptanceTests and put all the fitsharp files in there. Also change the project’s build settings to output into that folder as well.
- Create additional folders under the project for UnitTests and Fixtures. I’ll explain the Fixtures in a second.
- Create a file called fitness.config and put that in the bin folder. The contents of the file should be:
<suiteConfig> <ApplicationUnderTest> <AddAssembly> bin\GoGoGrocer.Specifications.dll </AddAssembly> <AddNamespace>fitlibrary</AddNamespace> <AddNamespace>system</AddNamespace> <AddNamespace> GoGoGrocer.Specifications.Fixtures </AddNamespace> </ApplicationUnderTest> <fitSharp.Fit.Application.FileExclusions> <Add>^\.svn$</Add> </fitSharp.Fit.Application.FileExclusions> </suiteConfig>
This adds the test assembly into the runner, and also provides a place to reference the namespaces of your fixtures. This makes it easy for you to reference them in your Fitnesse code. UPDATE: If your tests hang, make sure this file is encoded as UTF-8 (XML...sigh) - One more file to go. Open up the command prompt and run the startFitnesse.bat file. Among other things, this will install your FitnesseRoot and build the initial structure. Edit the content.txt file directly under the newly created FitnesseRoot folder.
!*> using fitsharp !define COMMAND_PATTERN {%m -c bin\fitnesse.config -r fitnesse.fitserver.FitServer,bin\fit.dll %p} !define TEST_RUNNER {bin\Runner.exe} *! !*> defines !define COLLAPSE_SETUP {true} !define COLLAPSE_TEARDOWN {true} *!
Hint: use the “Show All Files” feature in the Solution Explorer and add all the relevant folders to your project.
Now you’re ready to start using Fitnesse. When you’re all done with setup your project structure should look something like this:
Writing a Test in Flow Mode
The following content file defines the “Total Items” test shown above:
!2 Total Items Purchased The customer purchases items. Those items are scanned. The price of the item is put on the bill along with it's description. See figure 1. The subtotal is calculated as the sum of the price of all items on the bill and displayed at the bottom. !img-l http://sites.google.com/site/futureturnip/home/images/sub-total-receipt.png ![ TotalItemsPurchased The cashier scans "Wonder Bread" costing $3.49 The description "Wonder Bread" is displayed on the bill The bill shows that the "Wonder Bread" costs $3.49 The sub total shows $3.49 The cashier scans "Gallon Milk 2%" costing $3.89 The description "Gallon Milk 2%" is displayed on the bill The bill shows the cost of "Gallon Milk 2%" is $3.89 The sub total shows $7.38 The cashier scans "Bubbly Yummy" costing $0.79 The description "Bubbly Yummy" is displayed on the bill The bill shows the cost of "Bubbly Yummy" is $0.79 The sub total shows $8.17 ]!
The test is human-readable text, even in it’s wiki markup form. The trick is of course tying it to a Fixture. The fixture is a bridge between the Fitnesse runner and your code. The plain-text table ties itself to the TotalItemsPurchased class on it’s opening declaration. In that class a method is defined for every line in the text; replacing spaces with underscores and generally ignoring special characters like quotes or the percent sign (it did take a little experimentation to find that out). Here’s what fixture code might look like:
namespace GoGoGrocer.Specifications.Fixtures { public class TotalItemsPurchased { public bool The_cashier_scans_Wonder_Bread_costing_3_49() { // This method is just the hook. // Fire up your API and test the SUT. } public bool The_description_Wonder_Bread_is_displayed_on_the_bill() { return true; }
The methods return true for a passing test. That’s a little quirky, and there are opportunities to improve the reporting, but that’s the bridge into your test code.
Summary
I still have some little issues with Fitnesse, and I still think the Ruby folk have us beat with Cucumber, but for .net Fitnesse offers a pretty-good solution for acceptance tests.
I also tried to “Export Template…” this, but the resulting template doesn’t install itself into Visual Studio. Haven’t figured that out yet, but I’ll share if I ever do.
I am sorry that I have to say that, but this looks so wrong to me.
ReplyDeleteAre you really hard coding the prices and the name of the product into the method names? Do you also have methods named "The_sub_total_shows_3_49" and The_sub_total_shows_8_17"? This means that you cannot change your acceptance test without renaming or adding methods in the fixture.
This surely is not the purpose of FitNesse.
You could keep your test format, but change the fixture implementation. With the Slim test system you can add scenarios that match your style. The new ScenarioTable syntax that allows in-text parameters is not in the UserGuide on the fitnesse homepage yet, but you can find it here:
http://github.com/unclebob/fitnesse/blob/master/FitNesseRoot/FitNesse/UserGuide/SliM/ScenarioTable/content.txt
The format is a nice idea, but your implementation is completely missing the point of FitNesse to have reusable fixtures for multiple test cases.
Gregor
Gregor, you're absolutely right. I should have explained my self a little better in the example, gotten rid of the example test, or made it more vanilla. This post is really about the setup. I just posted a follow-up that talks about my current views on test style -- for what those are worth.
ReplyDeleteHi Jeff, I'm working on a series of FitNesse posts and I like what you did in terms of building FitNesse into the .Net Solution. I've set up my sample code in a similar way for my posts. Thanks.
ReplyDeletehttp://www.devjoy.com/2011/08/adding-fitnesse-to-your-net-solution/