Welcome, guest | Sign In | My Account | Store | Cart
NOTE: Recipes have moved! Please visit GitHub.com/activestate/code for the current versions.

This "tested" decorator assigns decorated_function.test() to call doctest.run_docstring_examples appropriately.

Python, 40 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
def tested(fun=None, pre=None, post=None, verbose=False, globs=None):
    ''' 
        >>> def my_pre(): print "this will be printed before running tests"
        ... 
        >>> def my_post(): print "this will be printed after running tests"
        ... 
        >>> @tested(pre=my_pre, post=my_post, verbose=True)
        ... def foo():
        ...     """ 
        ...         >>> foo()
        ...         True
        ...     """
        ...     return True
        >>> foo.test()
        this will be printed before running tests
        Finding tests in foo
        Trying:
            foo()
        Expecting:
            True
        ok
        this will be printed after running tests
    '''
    from sys import _getframe
    from doctest import run_docstring_examples
    
    if globs is None:
        globs = _getframe(1).f_locals
    if fun:
        def test():
            if callable(pre):
                pre()
            run_docstring_examples(fun, globs=globs, verbose=verbose, name=fun.__name__)
            if callable(post):
                post()
        fun.test = test
        return fun
    else:
        return lambda fun: tested(fun, pre=pre, post=post, verbose=verbose, globs=globs)
tested = tested(tested, verbose=True)

You can't debug all of your functions at once. It makes no sense to run all of a module's doctests at once. You just want to run one function's doctests, tweak it, rinse and repeat until that function passes, then move on to another function, rinse and repeat until your whole application. Known issues include head aches while trying to understand the implicit recursion in "tested.test()".

1 comment

Edward Loper 9 years, 7 months ago  # | flag

why use caller's locals as globals? What benefit do you get from using the caller's locals as globals for the tests? If anything, it seems like the default should be to use the decorated function's globals (i.e., fun.func_globals).