Saturday, January 08, 2005

PyFIT Tutorial Part 3

I will expand here on Part 1 and Part 2 of the PyFIT tutorial and I'll show how to use SetUp pages in FitNesse. I'll also clean up the code in some of the fixtures I wrote for the Blog Management application that I used as the SUT (Software Under Test).

First, some code cleanup. I had a lot of hard-coded paths in my fixtures. All fixtures used to start with:

This is clearly sub-optimal and requires a lot of copy-and-paste among modules. To simplify it, I turned the blogger directory into a package by simply adding to that directory an empty file called __init__.py. I also moved Blogger.py (the main functionality module) and its unit test module, testBlogger.py, to a subdirectory of blogger called src. I made that subdirectory a package too by adding to it another empty __init__.py file. Now each fixture module can do:

There's one caveat here: the parent directory of the blogger directory -- in my case C:\eclipse\workspace -- needs to be somewhere on the Python module search path. In FitNesse it's easy to solve this issue by adding C:\eclipse\workspace to the classpath via this variable definition which will go on the main suite page:

!path C:\eclipse\workspace

When invoking the Python interpreter from a command line, one way of making sure that C:\eclipse\workspace is in the module search path is to add it to the PYTHONPATH environment variable, for example in a .bash_profile file. For our example though, the fixture modules are always invoked within the FitNesse/PyFIT framework, so we don't need to worry about PYTHONPATH.

Now to the SetUp page functionality. If you create this page as a sub-page of a suite, then every test page in that suite will automatically have SetUp prepended to it by FitNesse. I created a SetUp page (its URL is http://localhost/FitNesse.BlogMgmtSuite.SetUp) with the following content:

!|BloggerFixtures.Setup|
|setup?|
|true|

I also created the corresponding Setup.py fixture, with the following code:

The setup method invokes the get_blog function from the Blogger module in order to retrieve the common Blogger instance used by all the fixtures in our test suite. I did this because I wanted to encapsulate the common object creation in one fixture class (Setup), then have all the other fixture classes cal the get_blog_manager function from the Setup module. Another benefit is that only the Setup module needs to know about the physical location of the Blogger module, via the line:

from blogger.src.Blogger import get_blog

All other fixture classes need only do:

from Setup import get_blog_manager

since they are in the same directory with Setup.py.

A SetUp page can also be used to pass values to the application via methods in the Setup class. Assume we need to pass the path to a configuration file. One way of accomplishing this is to define a FitNesse variable in the SetUp page, like this:

!define CONFIG_PATH {C:\config}

The FitNesse syntax for referencing a variable is ${variable}, so we can pass it as an argument to our Setup fixture like this:

!|BloggerFixtures.Setup|${CONFIG_PATH}|
|setup?|
|true|

When the page is rendered by FitNesse, ${CONFIG_PATH} is automatically replaced with its value, so on the rendered page we'll see:

variable defined: CONFIG_PATH=C:\config

BloggerFixtures.Setup

C:\config

setup?

true

In the Setup fixture class, we can get the value of the argument like this:

config_path = self.getArgs()[0]

One other thing we could do in the SetUp page is to include the DeleteAllEntries fixture, so that we can be sure that each test page will start with a clean slate in terms of the blog entries.

I moved the old code from parts 1 and 2 of the tutorial here. You can see the new code here.