Monday, 30 March 2009

Waferslim 0.9.1 released

This is a minor point-release, including fixes for unicode handling (in the python2.5 branch only), an additional server startup option --encoding, and usage notes in the README file. It's available from the usual places (Python cheese shop and Launchpad).

Tuesday, 17 March 2009

Using waferslim with fitnesse

Starting waferslim

Fitnesse communicates with waferslim using TCP sockets and needs to start an instance of the waferslim server each time a runnable page ("test" or "suite") is executed. To instruct fitnesse to use waferslim, your top-level wiki page needs to contain the following instructions:

!define TEST_SYSTEM {slim}
!path /some/path/to/src
!define COMMAND_PATTERN {python3 -m waferslim.server --syspath %p }

This tells fitnesse to use the slim protocol, and to start the waferslim server with sys.path as listed in "!path". For multiple path entries, separate each path entry with the OS-specific path separator (the python os.pathsep value i.e. ";" for windows, ":" otherwise). To use the python2.5 branch of waferslim the command pattern should obviously invoke python not python3.
Additional start-up parameters for the waferslim server can be specified in the COMMAND_PATTERN:
-h, --help
   see this list of options
-p PORT, --port=PORT
   listen on port PORT (see below)
-i HOST, --inethost=HOST
   listen on inet address HOST (default: localhost)
-e ENCODING, --encoding=ENCODING
   use byte-encoding ENCODING (default: utf-8)
-v, --verbose
   log verbose messages at runtime (default: False)
-k, --keepalive
   keep the server alive to service multiple requests, requires forked fitnesse code (default: False)
-l FILE, --logconf=FILE
   use logging configuration from FILE

Fitnesse itself supplies the port number by appending it to the end of the COMMAND_PATTERN: a "trailing" numeric value is assumed to be this port number if none is specified explicitly, so the following are equivalent (though the former is preferable):

!define COMMAND_PATTERN {python3 -m waferslim.server --syspath %p }
!define COMMAND_PATTERN {python3 -m waferslim.server --syspath %p --port }

Executing python code from waferslim

Fitnesse offers a variety of test table styles, all of which are supported by waferslim. Each table style causes waferslim to interact with the "system under test" - your python code - in a slightly different way. Examples of each of the table styles are provided in the sub-package waferslim.examples which is intended as a useful reference guide (viewable online.)
In broad terms waferslim interacts with the system under test as follows:
  1. waferslim creates an instance of a class in your python code (an "import" table in your fitnesse page helps waferslim to find and load the class)
  2. waferslim invokes one or more methods on that class instance, (where the method name is specified in the fitnesse table, usually in the column header), and values from the cells in the fitnesse table are passed as parameters
  3. a return value from a method invocation is passed back to fitnesse which interprets it as a pass (green), fail (red) or error (yellow).

Because the fitnesse slim protocol is string-based, all method parameters
passed from fitnesse cells will be unicode strings by default (type "str" in python3, type "unicode" in python2) .
The waferslim.converters module provides a method decorator to simplify the
conversion of these parameters to native python types such as int, float,
bool and datetime, e.g.

from waferslim.converters import convert_arg
class SomeClass:
    def some_method(self, int_param)
    @convert_arg(to_type=(int, bool))
    def another_method(self, int_param, bool_param)

Method return values passed back to fitnesse must also be converted into strings (again, unicode by default). This conversion should be transparent unless you have a class for which str(instance) does not provide the required conversion. Converters for types not already handled by waferslim may be registered using the register_converter function, e.g.:

from waferslim.converters import register_converter
register_converter(for_type, converter_instance)

The converter_instance is an object (which may subclass waferslim.converters.Converter) that has 2 specific methods:

def from_string(self, value): ... # returns type-instance
def to_string(self, value): ... # returns str-instance

An alternative to registering a custom converter (which will be used for all the fitnesse test pages in a suite) is to use a "temporary" converter by passing the named argument using=a_converter_instance to the @convert_arg and/or @convert_result method decorators. This is recommended for fitnesse tables that use the alternative boolean "Yes-No" converter, as in the example classes in waferslim.examples.decision_table. (Registering YesNoConverter for bool instances would override the default bool converter TrueFalseConverter and cause incorrect behaviour in any script tables run in the same suite, as script tables require "True"/"False" values to be returned to fitnesse for bools).

Wednesday, 11 March 2009

Introducing... waferslim

WaferSlim is a python port of the fitnesse slim server and protocol. Packages for both python2.5 and python3 are distributed through the Python Cheese Shop and the latest source code is available at launchpad.
As well as the code needed to write and run your own fixtures, examples of fixtures for decision, script, query and table tables are provided. While the code is believed to be "feature complete" feedback on 'real world' usage would be welcomed!

Release 0.9 provides:
- a runnable socket server that unpacks slim-protocol messages sent from a fitnesse server and sends back the packed slim-protcol fixture results
- implementations of the slim Import, Make, Call and CallAndAssign instructions
- converter classes and method decorators to make working with native python datatypes easier
- an isolated execution context that dynamically loads systems-under-test in a way that separates them from each other (and opens the door to a fitnesse command runner that does not need to spawn a new process for each suite or page run)
- examples of fixtures for decision, script, query and table tables
- full docstrings and pylint score > 9
- high pycoverage

Lancelot 1.0 released

Python3 and 2.5 packages are available at the Python Cheese Shop The launchpad release summary has the details, and there are only a couple of changes from the RC2 release:
  • added ability to give MockSpec instances meaningful names that appear in unmet specification messages (useful when several colloborations are being specified)
  • bug fix 341071: after will_raise() raised exception the mock would not verify()
  • bug fix 31073: after specification unmet would not generate meaningful error messages for tuple comparisons