tag:blogger.com,1999:blog-48320239217588850832024-02-08T02:18:19.686-08:00...with a herring!Test-driven development for Pythontimbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-4832023921758885083.post-90060541423675797402010-03-28T08:08:00.000-07:002010-03-28T08:10:26.575-07:00Waferslim 1.0.2 releasedThis is a bug fix release targeting:<br /><ul><li><a href="https://bugs.launchpad.net/bugs/550249">Return value zero is shown as __VOID__</a></li></ul><br />It's available from the usual places (<a href="http://pypi.python.org/pypi/waferslim/">Python cheese shop</a> and <a href="https://launchpad.net/waferslim">Launchpad</a>).timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com2tag:blogger.com,1999:blog-4832023921758885083.post-91022134570857054442010-03-10T18:38:00.000-08:002010-03-10T18:46:04.682-08:00Waferslim 1.0.1 releasedThis is a bug fix release targeting:<br /><ul><li><a href="https://bugs.launchpad.net/bugs/537020">Undefined symbols in tables raise an exception</a></li><br /><li><a href="https://bugs.launchpad.net/bugs/537026">Multi-line data in fitnesse table cells causes exception</a></li><br /><li><a href="https://bugs.launchpad.net/bugs/537032">Test fails when __repr__ raises exception while logging</a></li></ul><br />The release also includes an <a href="http://bazaar.launchpad.net/%7Eprime8/waferslim/main/annotate/head%3A/src/waferslim/examples/method_names.py">additional code sample</a> to illustrate how method name lookup works.<br />It's available from the usual places (<a href="http://pypi.python.org/pypi/waferslim/">Python cheese shop</a> and <a href="https://launchpad.net/waferslim">Launchpad</a>).timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-86148056714028061602010-02-27T17:15:00.000-08:002010-02-27T19:02:00.505-08:00Waferslim 1.0 releasedThis major release supports fitnesse release 2010-01-03 and slim protocol v0.1:<br /><ul><br /><li>Added support for delegating from fixture to system under test with fixture.sut()</li><br /><li>Added support for library fixtures</li><br /><li>Added <code>EchoFixture</code> for use as a library fixture</li><br /><li>Added support for HashMarkup dicts</li><br /><li>Added <code>StopTestException</code> and handling</li><br /><li>Dynamic module loading no longer the default: fixed bug 497245 (unable to import twisted)</li><br /><li>Added support for complex variable substitution: fixed problem with e.g. <code>$id$subid</code></li><br /><li>Updated <code>TableTableConstants</code> to add <code>cell_ignore()</code> and <code>cell_report()</code></li><br /><li>Added <code>from_string()</code> and <code>to_string()</code> functions to <code>waferslim.converters</code></li><br /><li>Tweaked method name invocation so that leading underscores in method names are no longer expected if the fitnesse cell value contains <code>Upper Case Values</code></li><br /><li>Added examples for all the new features</li><br /></ul><br />It's available from the usual places (<a href="http://pypi.python.org/pypi/waferslim">Python cheese shop</a> and <a href="http://launchpad.net/waferslim">Launchpad</a>).timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-57646581060659704672009-03-30T06:04:00.000-07:002009-03-30T06:18:34.604-07:00Waferslim 0.9.1 releasedThis is a minor point-release, including fixes for unicode handling (in the python2.5 branch only), an additional server startup option <em>--encoding</em>, and usage notes in the README file. It's available from the usual places (<a href="http://pypi.python.org/pypi/waferslim">Python cheese shop</a> and <a href="https://code.launchpad.net/waferslim">Launchpad</a>).timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com3tag:blogger.com,1999:blog-4832023921758885083.post-79221738222222620212009-03-17T07:01:00.000-07:002009-03-30T06:12:42.030-07:00Using waferslim with fitnesse<h4>Starting waferslim</h4><br />Fitnesse communicates with waferslim using TCP sockets and needs to start an instance of the waferslim server each time a runnable page (<em>"test"</em> or <em>"suite"</em>) is executed. To instruct fitnesse to use waferslim, your top-level wiki page needs to contain the following instructions:<br /><code><br /> !define TEST_SYSTEM {slim} <br /> !path /some/path/to/src<br /> !define COMMAND_PATTERN {python3 -m waferslim.server --syspath %p }<br /></code><br />This tells fitnesse to use the slim protocol, and to start the waferslim server with <em>sys.path</em> as listed in "!path". For multiple path entries, separate each path entry with the OS-specific path separator (the python <em>os.pathsep</em> value i.e. ";" for windows, ":" otherwise). To use the python2.5 branch of waferslim the command pattern should obviously invoke <em>python</em> not <em>python3</em>.<br />Additional start-up parameters for the waferslim server can be specified in the <em>COMMAND_PATTERN</em>:<code> <br />-h, --help<br /> see this list of options<br />-p PORT, --port=PORT <br /> listen on port PORT (see below)<br />-i HOST, --inethost=HOST<br /> listen on inet address HOST (default: localhost)<br />-e ENCODING, --encoding=ENCODING <br /> use byte-encoding ENCODING (default: utf-8)<br />-v, --verbose<br /> log verbose messages at runtime (default: False)<br />-k, --keepalive<br /> keep the server alive to service multiple requests, requires forked fitnesse code (default: False)<br />-l FILE, --logconf=FILE<br /> use logging configuration from FILE<br /></code><br />Fitnesse itself supplies the port number by appending it to the end of the <em>COMMAND_PATTERN</em>: 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):<br /><code><br /> !define COMMAND_PATTERN {python3 -m waferslim.server --syspath %p }<br /> !define COMMAND_PATTERN {python3 -m waferslim.server --syspath %p --port }</code><br /><h4>Executing python code from waferslim</h4><br />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 <em>waferslim.examples</em> which is intended as a useful reference guide (viewable <a href="http://bazaar.launchpad.net/~prime8/waferslim/main/files/head%3A/src/waferslim/examples">online</a>.) <br />In broad terms waferslim interacts with the system under test as follows:<ol><li>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)</li><li>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</li><li>a return value from a method invocation is passed back to fitnesse which interprets it as a pass (green), fail (red) or error (yellow).</li></ol><br />Because the fitnesse slim protocol is string-based, all method parameters<br />passed from fitnesse cells will be unicode strings by default (type "str" in python3, type "unicode" in python2) . <br />The <em>waferslim.converters</em> module provides a method decorator to simplify the <br />conversion of these parameters to native python types such as int, float, <br />bool and datetime, e.g.<br /><code><br />from waferslim.converters import convert_arg<br />...<br /><em>class SomeClass</em>:<br /> <strong>@convert_arg(to_type=int)</strong><br /> def some_method(self, int_param)<br /> ...<br /> <strong>@convert_arg(to_type=(int, bool))</strong><br /> def another_method(self, int_param, bool_param)<br /> ...<br /></code><br />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 <em>str(instance)</em> does not provide the required conversion. Converters for types not already handled by waferslim may be registered using the <em>register_converter</em> function, e.g.:<br /><code><br /> from waferslim.converters import register_converter<br /> ...<br /> <strong>register_converter(for_type, converter_instance)</strong><br /></code><br />The <em>converter_instance</em> is an object (which may subclass <em>waferslim.converters.Converter</em>) that has 2 specific methods:<br /><code><br /> def <strong>from_string</strong>(self, value): ... # returns type-instance<br /> def <strong>to_string</strong>(self, value): ... # returns str-instance<br /></code><br />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 <strong>using=a_converter_instance</strong> to the <em>@convert_arg</em> and/or <em>@convert_result</em> method decorators. This is recommended for fitnesse tables that use the alternative boolean "Yes-No" converter, as in the example classes in <em><a href="http://bazaar.launchpad.net/~prime8/waferslim/main/annotate/head%3A/src/waferslim/examples/decision_table.py">waferslim.examples.decision_table</em></a>. (Registering <em>YesNoConverter</em> for bool instances would override the default bool converter <em>TrueFalseConverter</em> 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).timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-73229348250148746792009-03-11T06:56:00.001-07:002009-03-11T10:46:51.542-07:00Introducing... waferslimWaferSlim is a python port of the <a href="http://fitnesse.org/FitNesse.SliM">fitnesse slim server and protocol</a>. Packages for both python2.5 and python3 are distributed through the <a href="http://pypi.python.org/pypi/waferslim/">Python Cheese Shop</a> and the latest source code is available at <a href="https://launchpad.net/waferslim">launchpad</a>.<br />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!<br /><br />Release 0.9 provides:<br />- a runnable socket server that unpacks slim-protocol messages sent from a fitnesse server and sends back the packed slim-protcol fixture results<br />- implementations of the slim Import, Make, Call and CallAndAssign instructions<br />- converter classes and method decorators to make working with native python datatypes easier<br />- 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)<br />- examples of fixtures for decision, script, query and table tables<br />- full docstrings and pylint score > 9<br />- high pycoveragetimbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-10257524627097784122009-03-11T06:22:00.000-07:002009-03-11T06:56:34.925-07:00Lancelot 1.0 releasedPython3 and 2.5 packages are available at the <a href="http://pypi.python.org/pypi/lancelot">Python Cheese Shop</a> The <a href="http://launchpad.net/lancelot/trunk/1.0">launchpad release summary</a> has the details, and there are only a couple of changes from the <a href="http://withaherring.blogspot.com/2009/02/lancelot-10rc2-available.html">RC2 release</a>:<br /><ul><li>added ability to give MockSpec instances meaningful names that appear in unmet specification messages (useful when several colloborations are being specified)</li><li>bug fix 341071: after will_raise() raised exception the mock would not verify()</li><li>bug fix 31073: after specification unmet would not generate meaningful error messages for tuple comparisons</li></ul>timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-49815251278578425142009-02-20T09:30:00.001-08:002009-02-20T09:45:57.159-08:00Upcoming features - some thoughtsI've got some ideas in mind for the next release of lancelot:<ul><li>Provide a bridge (in both directions) between unittest and lancelot, so that lancelot specs can be run as if they were TestCase-s, and unit tests can be verified in lancelot.</li><li>Allow documentation to be generated from specs as they are run.</li><li>Add some syntactic sugar to improve the readability of "given/when/then..." statements</li><li>Introduce a spec mutator similar to <a href="http://jester.sourceforge.net/">Pester</a> (and Jester), to ensure confidence in verified specs</li></ul><br />I won't have as much free time to work on this next release so I doubt whether more than one of these will make it into the codebase over the next 4 weeks... we'll see! :-)timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-1992882400959863172009-02-20T09:15:00.000-08:002009-02-20T09:27:23.881-08:00Lancelot 1.0RC2 availablePython3 and 2.5 packages are available at the <a href="http://pypi.python.org/pypi/lancelot">Python Cheese Shop</a> Here are the release details, taken from the <a href="http://launchpad.net/lancelot/trunk/1.0rc2">launchpad release summary</a>:<br /><em><br />This release is intended to be the last before the final 1.0 release. It includes:<ul><li>a Spec class to allow BDD-style specifications (given, when, then...) to be made about the behaviour of an object instance or standalone-function (e.g. should_be, should_contain, should_raise, should_collaborate_with)</li><li>a MockSpec class to allow collaborations with other object instances to be specified</li><li>an @verifiable function decorator to allow specifications to be verified</li><li>an @grouping class decorator to allow specifications to be logically grouped together</li><li>a verify() function to use when verifying one or all verifiable specifications</li><li>a number of Comparable classes to use in specifications</li><li>example code to illustrate the various features</li><li>package, class and method docstrings</li><li>a comprehensive set of specifications for the specification classes themselves</li></ul></em><br /><br />Changes made since rc1 are generally minor:<ul><li>new <em>MockResult</em> class has been added, to clarify <em>will_return()</em> behaviour and allow <em>will_raise()</em> statements</li><li>new <em>@grouping</em> class decorator has been added, to allow <em>@verifiable</em> specifications to be logically grouped together</li><li><em>fail_fast</em> option has been added to <em>verify()</em> function</li><li><em>it()</em> method has been added to <em>Spec</em> class</li><li>there's been a general tidying-up of code, docstrings and specs to keep pylint score above 9.0 and pycoverage at 99%</li></ul>timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-77237953480573862472009-02-18T02:58:00.000-08:002009-02-18T03:30:10.919-08:00A couple of SpecsHere are a couple of snippets of sample code for <strong>lancelot</strong>.<br /><br />First up, a standalone function:<br /><code>import lancelot<br /><br />def <strong>fib</strong>(ordinal=0):<br /> '''<em>Simple fibonacci generator</em>'''<br /> ...<br /><br />@lancelot.verifiable<br />def <strong>specify_fib_zero_to_five</strong>():<br /> '''<em>First five fibonacci numbers should be 1,2,3,5,8</em>'''<br /> spec = lancelot.Spec(fib)<br /> spec.fib(1).should_be(1)<br /> spec.fib(2).should_be(2)<br /> spec.fib(3).should_be(3)<br /> spec.fib(4).should_be(5)<br /> spec.fib(5).should_be(8)<br /></code> <br /><br />Next, a standalone class:<br /><code>import lancelot<br /><br />class <strong>Stack</strong>:<br /> '''<em>A simple stack with push, pop and peek</em>'''<br /> ...<br /><br />@lancelot.verifiable<br />def <strong>behavior_of_stack_with_values</strong>():<br /> '''<em>Should be able to peek() and pop() values after push()</em>'''<br /> stack = lancelot.Spec(Stack, given=new_stack)<br /> stack.when(stack.push(value='a'))<br /> stack.then(stack.peek()).should_be('a')<br /> stack.then(stack.pop()).should_be('a')<br /> stack.then(stack.peek()).should_raise(IndexError)<br /> stack.then(stack.pop()).should_raise(IndexError)<br /></code> <br /><br />Finally, a class whose behaviour involves <a href="http://c2.com/doc/oopsla89/paper.html">collaborations</a> with other classes:<br /><code>import lancelot<br /><br />class <strong>Observable</strong>:<br /> '''<em>Simple class that sends notifications to its observers</em>'''<br /> ...<br /><br />@lancelot.verifiable<br />def <strong>observable_observer_behaviour</strong>():<br /> '''<em>Added Observers should receive Observer notifications.</em>'''<br /> observer = lancelot.MockSpec()<br /> observable = Observable()<br /> spec = lancelot.Spec(observable)<br /> spec.when(spec.add_observer(observer))<br /> spec.then(spec.send_notification())<br /> spec.should_collaborate_with(observer.notify(observable))<br /></code>timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-15661184906880403702009-02-17T14:15:00.000-08:002009-02-18T03:25:39.484-08:00Introducing... LancelotI've been working on <strong><a href="http://pypi.python.org/pypi/lancelot">lancelot</a></strong>: a specification and verification library for python, inspired by the particular idiom of test driven development that goes under the banner of BDD. (And the word <em>idiom</em> is a <a href="http://www.mwscomp.com/movies/grail/grail-16.htm">film reference</a>: Sir Lancelot: "Um, I think when I'm in this idiom, I sometimes get a bit, uh, sort of carried away.")<br /><br />The code is available in a launchpad bazaar repository at <a href="http://launchpad.net/lancelot">http://launchpad.net/lancelot</a>, and the packages can be downloaded from the Cheese Shop at <a href="http://pypi.python.org/pypi/lancelot">http://pypi.python.org/pypi/lancelot</a>. Versions for both Python 3.x and 2.5 are available: if you check them out please let me know what you think!<br /><br />PS: Although I'm aware of <a href="http://www.codeplex.com/pyspec">PySpec</a> and <a href="http://colus.egloos.com/3671032">SpeciPy</a>, I haven't actually used them, so I can't really compare <strong>lancelot</strong> with what they offer. My motivation for starting a new project was that I wanted something to use with Python 3, and I thought I could put my long experience with TDD to good use. (Perhaps I should qualify "long experience"... I've been using JUnit since 2000, was a committer on the Java <a href="http://xmlunit.sourceforge.net/">XmlUnit</a> project, and in 2003-4 I had the pleasure of working alongside <a href="http://www.sanityinc.com/">Steve Purcell</a>, the author of the Python unittest package, and Nat Pryce, Steve Freeman and Joe Walnes, the authors of the JMock framework. More recently, after some discussion with <a href="http://thediscoblog.com/">Andrew Glover</a>, I have been experimenting with <a href="http://easyb.org">easyb</a>, the Java/Groovy BDD framework).timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0tag:blogger.com,1999:blog-4832023921758885083.post-85435476327191125862009-02-17T12:04:00.000-08:002009-02-17T12:16:14.136-08:00OriginsThe title of the blog refers to a <a href="http://www.google.com/search?&q=herring+grail+knights+ni+python">scene</a> from one of the Monty Python films. Because, sometimes, practicing test-driven development in Python (compared to say Java or Ruby) does feel a little like being asked to cut down a mighty tree with a fish...timbaconhttp://www.blogger.com/profile/01876156948492933493noreply@blogger.com0