A new syntax for writing tests

Jonathan Fine jfine at pytex.org
Wed Aug 4 16:15:33 EDT 2010


Hi

I just discovered today a new syntax for writing tests.  The basic idea 
is to write a function that contains some statements, and run it via a 
decorator.  I wonder if anyone had seen this pattern before, and how you 
feel about it.  For myself, I quite like it.

Let's suppose we want to test this trivial (of course) class.
     class Adder(object):

         def __init__(self):
             self.value = 0

         def plus(self, delta):
             self.value += delta

The test the class you need a runner.  In this case it is quite simple.

     def runner(script, expect):
         '''Create an adder, run script, expect value.'''

         adder = Adder()
         script(adder)
         return adder.value

We can now create (and run if we wish) a test.  To do this we write

     @testit(runner, 4)
     def whatever(a):
         '''Two plus two is four.'''

         a.plus(2)
         a.plus(2)

Depending on the exact value of the testit decorator (which in the end 
is up to you) we can store the test, or execute it immediately, or do 
something else.

The simplest implementation prints:
     OK: Two plus two is four.
for this passing test, and
     Fail: Two plus four is five.
       expect 5
       actual 6
for a test that fails.

Here is the testit decorator used to produce the above output:

     def testit(runner, expect):
         '''Test statements decorator.'''

         def next(script):
             actual = runner(script, expect)
             if actual == expect:
                 print 'OK:', script.__doc__
             else:
                 print 'Fail:', script.__doc__
                 print '  expect', expect
                 print '  actual', actual

         return next


You can pick this code, for at least the next 30 days, at
     http://dpaste.com/hold/225056/

For me the key benefit is that writing the test is really easy.  Here's 
a test I wrote earlier today.

@testit(runner, '''<a att="value"><b/></a>''')
def whatever(tb):
     tb.start('a', {'att': 'value'})
     tb.start('b')
     tb.end('b')
     tb.end('a')

If the test has a set-up and tear-down, this can be handled in the 
runner, as can the test script raising an expected or unexpected exception.

-- 
Jonathan



More information about the Python-list mailing list