Welcome, guest | Sign In | My Account | Store | Cart

Inspired by py.test, the unittester is a single, simple function that is easily customized. The main virtue is that tests are much clearer than with the unittest.py.

Python, 73 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def run_test_functions(source=None):
    from sys import stderr
    from types import GeneratorType

    # source can be module, class, None, or a dictionary    
    if source is None:
        ns = globals()
    elif isinstance(source, dict):
        ns = dict(source)
    else:
        ns = vars(source)

    tests = [(name, func) for name, func in ns.items() if name.startswith('test_')]
    tests = sorted(tests, reverse=True)

    successes = failures = 0
    while tests:
        name, func = tests.pop()
        try:
            rv = func()
        except Exception, E:
            failures += 1
            print name, '...', repr(E)
        else:
            if isinstance(rv, GeneratorType):
                pairs = []
                for func, arg in rv:
                    gname = 'gen:%s(%r)' % (name, arg)
                    gfunc = lambda func=func, arg=arg: func(arg)
                    pairs.append((gname, gfunc))
                tests.extend(reversed(pairs))
            else:
                successes += 1
                print name, '... Success'

    result = (successes+failures, failures)
    print 'Ran %d tests with %d failures' % result
    return result


#################################
## Example of the tester in action

def test_sum():
    assert sum(range(5)) == 10

def test_badsum():
    assert sum(range(5)) == 12

def test_excpt():
    raise IndexError(3)

def test_generative():
    for x in (42,17,49):
        yield check, x

def check(arg):
    assert arg % 7 == 0

def td_setup():
    global g
    g = 1

def td_teardown():
    global g
    del g

def test_setup_and_teardown():
    td_setup()
    assert g == 1
    td_teardown()

print run_test_functions()

Supports the most popular features offered by py.test. See: http://codespeak.net/py/dist/test.html#starting-point-py-test-command-line-tool

Provides simply formatted output. For example, the example code above produces:

test_badsum ... AssertionError()
test_excpt ... IndexError(3,)
gen:test_generative(42) ... Success
gen:test_generative(17) ... AssertionError()
gen:test_generative(49) ... Success
test_setup_and_teardown ... Success
test_sum ... Success
Ran 7 tests with 3 failures
(7, 3)

Unlike py.test, the tool itself is very simple (a single 38 line function). Being so short, it is easily extended or altered to work with various test drivers and reporters.

The style of writing tests is much simpler than with the unittest.py module in the standard library. Compare the unittest.py style:

class TestStats(unittest.TestSuite):
    def test_sum(self):
        self.assertEqual(sum(range(5)), 10)

to the py.test style:

def test_sum():
    assert sum(range(5)) == 10

Also, if tests fail, it's easy to add regular print-statements for debugging. You can't do that with unittest.py because it intercepts stdout and stderr.

3 comments

Justin Shaw 13 years, 6 months ago  # | flag
chris skoni 13 years, 6 months ago  # | flag

functools - where are they ? I get this error in PYTHON 2.4.3:

Traceback (most recent call last): File "", line 1, in -toplevel- import functools ImportError: No module named functools

Nick Coghlan 13 years, 4 months ago  # | flag

functools is a new module added to the standard library in Python 2.5