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:

from fit.ColumnFixture import ColumnFixture
import sys
blogger_path = "C:\\eclipse\\workspace\\blogger"
sys.path.append(blogger_path)
import Blogger

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:

from fit.ColumnFixture import ColumnFixture
from blogger.src.Blogger import get_blog

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:

from fit.ColumnFixture import ColumnFixture
from blogger.src.Blogger import get_blog

blog_manager = None

class Setup(ColumnFixture):
_typeDict={
"setup": "Boolean"
}

def setup(self):
global blog_manager
blog_manager = get_blog()
return (blog_manager != None)

def get_blog_manager():
global blog_manager
return blog_manager

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.

Modifying EC2 security groups via AWS Lambda functions

One task that comes up again and again is adding, removing or updating source CIDR blocks in various security groups in an EC2 infrastructur...