Skip to content

Commit

Permalink
make doctestfiles into a class decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim Fulton committed Aug 31, 2015
1 parent f03c227 commit 5886813
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 90 deletions.
8 changes: 6 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ Changes

- Automatically assign test class members. So rather than::

test_foo = doctestcase.doctestfile('foo.txt')
class MYTests(unittest.TestCase):
...
test_foo = doctestcase.doctestfile('foo.txt')

You can use::

doctestcase.doctestfiles('foo.txt')
@doctestcase.doctestfiles('foo.txt')
class MYTests(unittest.TestCase):
...

4.4.0 (2015-07-16)
------------------
Expand Down
22 changes: 9 additions & 13 deletions src/zope/testing/doctestcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@ def test_file_w_setup(self):
file = doctestfile

def doctestfiles(*paths, **kw):
"""Define doctests from test files within a unittest.TestCase.
"""Define doctests from test files in a decorated class.
Multiple files can be specified. A member is added to the
surrounding class for each file.
decorated class for each file.
The file paths may be relative or absolute. If relative (the
common case), they will be interpreted relative to the directory
Expand All @@ -245,22 +245,18 @@ def doctestfiles(*paths, **kw):
The test object is available as the variable ``test`` in the test.
The resulting object can be used as a function decorator. The
decorated method is called before the test and may perform
test-specific setup. (The decorated method's doc string is ignored.)
The resulting object must be used as a class decorator.
"""
locals = sys._getframe(1).f_locals
for path in paths:
locals[name_from_path(path)] = doctestfile(path, **kw)

def doctestfiles_w_setup(func):
def doctestfiles_(class_):
for path in paths:
name = name_from_path(path)
test = doctestfile(path, **kw)(func)
locals[name] = test
test = doctestfile(path, **kw)
test.__name__ = name
setattr(class_, name, test)

return class_

return doctestfiles_w_setup
return doctestfiles_

files = doctestfiles

Expand Down
108 changes: 33 additions & 75 deletions src/zope/testing/doctestcase.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ Doctests in TestCase classes
The original ``doctest`` unittest integration was based on
``unittest`` test suites, which have fallen out of favor. This module
provides a way to define doctests inside of unittest ``TestCase``
classes. It also provides better integration with unittest test
fixtures, because doctests use setup provided by the containing test
case class. It also provides access to unittest assertion
methods.
classes. It provides better integration with unittest test fixtures,
because doctests use setup provided by the containing test case
class. It provides access to unittest assertion methods.

You can define doctests in 4 ways:
You can define doctests in multiple ways:

- references to named files

Expand All @@ -19,6 +18,8 @@ You can define doctests in 4 ways:

- reference to named files decorating test-specific setup functions

- reference to named files decorating a test class

.. some setup

>>> __name__ = 'tests'
Expand All @@ -30,6 +31,7 @@ Here are some examples::
>>> import unittest

>>> g = 'global'

>>> class MyTest(unittest.TestCase):
...
... def setUp(self):
Expand All @@ -54,23 +56,36 @@ Here are some examples::
... @doctestcase.doctestfile('test4.txt')
... def test4(self):
... self.x = 5

>>> import sys

>>> @doctestcase.doctestfiles('loggingsupport.txt', 'renormalizing.txt')
... class MoreTests(unittest.TestCase):
...
... doctestcase.doctestfiles('loggingsupport.txt', 'renormalizing.txt')
... def setUp(self):
... def print_(*args):
... sys.stdout.write(' '.join(map(str, args))+'\n')
... self.globs = dict(print_ = print_)


.. We can run these tests with the ``unittest`` test runner.

>>> loader = unittest.TestLoader()
>>> suite = loader.loadTestsFromTestCase(MyTest)
>>> import sys
>>> sys.stdout.writeln = lambda s: sys.stdout.write(s+'\n')
>>> suite = loader.loadTestsFromTestCase(MyTest)
>>> result = suite.run(unittest.TextTestResult(sys.stdout, True, 3))
test1 (tests.MyTest) ... ok
test2 (tests.MyTest) ... ok
test3 (tests.MyTest) ... ok
test4 (tests.MyTest) ... ok
test_loggingsupport (tests.MyTest) ... FAIL
test_renormalizing (tests.MyTest) ... FAIL

>>> for _, e in result.errors:
... print(e); print

>>> suite = loader.loadTestsFromTestCase(MoreTests)
>>> result = suite.run(unittest.TextTestResult(sys.stdout, True, 3))
test_loggingsupport (tests.MoreTests) ... ok
test_renormalizing (tests.MoreTests) ... ok

>>> for _, e in result.errors:
... print(e); print
Expand All @@ -97,70 +112,16 @@ Here are some examples::
>>> MyTest.test4.filename
'test4.txt'

>>> MyTest.test_loggingsupport.__name__
'test_loggingsupport'
>>> MyTest.test_loggingsupport.filename
'loggingsupport.txt'
>>> (MyTest.test_loggingsupport.filepath ==
... os.path.join(os.path.dirname(zope.testing.__file__),
... 'loggingsupport.txt'))
True

Need setup:

>>> import sys
>>> class MyTest(unittest.TestCase):
...
... def setUp(self):
... self.a = 1
... self.globs = dict(c=9)
...
... test1 = doctestcase.file('test-1.txt', optionflags=doctest.ELLIPSIS)
...
... test2 = doctestcase.docteststring('''
... >>> self.a, g, c
... (1, 'global', 9)
... ''')
...
... @doctestcase.doctestmethod(optionflags=doctest.ELLIPSIS)
... def test3(self):
... '''
... >>> self.a, self.x, g, c
... (1, 3, 'global', 9)
... '''
... self.x = 3
...
... @doctestcase.doctestfile('test4.txt')
... def test4(self):
... self.x = 5
...
... @doctestcase.files('loggingsupport.txt', 'renormalizing.txt')
... def setup_print(self):
... def print_(*args):
... sys.stdout.write(' '.join(map(str, args))+'\n')
... self.globs['print_'] = print_

>>> suite = loader.loadTestsFromTestCase(MyTest)
>>> result = suite.run(unittest.TextTestResult(sys.stdout, True, 3))
test1 (tests.MyTest) ... ok
test2 (tests.MyTest) ... ok
test3 (tests.MyTest) ... ok
test4 (tests.MyTest) ... ok
test_loggingsupport (tests.MyTest) ... ok
test_renormalizing (tests.MyTest) ... ok

Check meta data with setup function:

>>> MyTest.test_loggingsupport.__name__
>>> MoreTests.test_loggingsupport.__name__
'test_loggingsupport'
>>> MyTest.test_loggingsupport.filename
>>> MoreTests.test_loggingsupport.filename
'loggingsupport.txt'
>>> (MyTest.test_loggingsupport.filepath ==
>>> (MoreTests.test_loggingsupport.filepath ==
... os.path.join(os.path.dirname(zope.testing.__file__),
... 'loggingsupport.txt'))
True

In this example, 4 constructors were used:
In these examples, 4 constructors were used:

doctestfile (alias: file)
doctestfile makes a file-based test case.
Expand All @@ -170,14 +131,11 @@ doctestfile (alias: file)
setup.

doctestfiles (alias: files)
doctestfiles makes file-based test cases and assigns them to the class.
doctestfiles makes file-based test cases and assigns them to the
decorated class.

Multile files can be specified and the resulting doctests are inserted into
the class dictionary.

This can be used as a decorator, in which case, the decorated
function is called before each test is run, to provide test-specific
setup.
Multile files can be specified and the resulting doctests are added
as members of the decorated class.

docteststring (alias string)
docteststring constructs a doctest from a string.
Expand Down

0 comments on commit 5886813

Please sign in to comment.