[Dev] Unit test runner

Ted Leung twl at osafoundation.org
Fri Feb 4 16:30:42 PST 2005


Ok,

So I'm late with this commentary (to the point that the test runner, 
etc are already checked in).  So far I like what I see.

My only other comment is that I agree that we should be adding test 
methods in preference to test modules.

Ted

On Jan 26, 2005, at 1:03 PM, Phillip J. Eby wrote:

> Hi folks.  Lisa has asked me to put together a unit test runner that 
> will allow selective running of multiple unit tests, for example to 
> run just fast unit tests, or to run all tests including functional 
> tests.  I have a couple of different ways to do that, but I'd like to 
> get some input from the team as to what they'd like to see in such a 
> runner.  But before I get to the questions, I need to lay out some 
> background on Chandler's current test setup, and the options that are 
> available for how to proceed.
>
> Currently, the way most of Chandler's tests are structured, you can 
> run individual test modules with either hardhat or unittest.py, e.g.:
>
>     unittest.py repository.tests.TestText
>
> will run that specific test module, or:
>
>     unittest.py repository.tests.TestText.TestText.testBZ2Compressed
>
> to run an individual test within the module.  You can also use hardhat 
> to collect all test modules named 'Test*.py'.
>
> So, I gather that what Lisa's asking for is the ability to also run a 
> selected subset of the total collection of tests, such as just the 
> "strict" unit tests that test subsystems in isolation, with individual 
> runtimes of about 10ms or less.  (Most of Chandler's current tests are 
> technically speaking functional or integration tests, as they do not 
> test subsystems in isolation from each other.  These types of tests 
> are important also, but because they take longer to run, it's useful 
> to also have more "isolationist" tests for use during development.)
>
> Python's unittest module has a standard way of representing a 
> collection of tests, called a "suite".  The Python doctest module can 
> also create suites from embedded and external doctests.  (Embedded = 
> doctests in docstrings, external = doctests in text files.)  If you 
> write a function that returns a test suite, then that function can be 
> run with unittest.py.  For example, here's an excerpt from a unit test 
> module in PyProtocols  (protocols.tests.test_dispatch):
>
>     from unittest import TestCase, makeSuite, TestSuite
>     import doctest
>
>     # actual test classes omitted for brevity
>
>     TestClasses = (
>         TestGraph, TestTests, ExpressionTests, SimpleGenerics, 
> GenericTests,
>     )
>
>     def test_combiners():
>         return doctest.DocFileSuite(
>             'combiners.txt', optionflags=doctest.ELLIPSIS, 
> package='dispatch',
>         )
>
>     def test_suite():
>         return TestSuite(
>             [test_combiners(), ] +
>             [makeSuite(t,'test') for t in TestClasses]
>         )
>
> The two functions here, 'test_suite' and 'test_combiners' are 
> functions that return unittest "test suites".  One of them creates an 
> external doctest that runs the tests in 'combiners.txt' (a tutorial 
> text file), and the other creates a test suite that combines that test 
> with test suites for each of the other test classes in the module.
>
> Thus, to run all 50 unit tests specified by this test module, I can 
> now use:
>
>     unittest.py protocols.tests.test_dispatch.test_suite
>
> Or to run just the doctest, I can use:
>
>     unittest.py protocols.tests.test_dispatch.test_combiners
>
> And of course I can still also specify an individual test case class 
> or method.
>
> There are also a couple of other ways to gather tests.  For example, 
> this code defines a function that returns a suite of all the test 
> classes found in every module of the 'repository.tests' package:
>
>     from unittest import defaultTestLoader, TestSuite
>     import repository.tests
>
>     testNames = """
>     TestAlias
>     TestBZ2
>     TestBinary
>     TestDeepCopyRef
>     TestDelete
>     TestIndexes
>     TestItems
>     TestKinds
>     TestLiteralAttributes
>     TestMerge
>     TestMixins
>     TestMove
>     TestPerfWithRSS
>     TestPersistentCollections
>     TestRedirectToOrdering
>     TestRefDictAlias
>     TestReferenceAttributes
>     TestRepository
>     TestRepositoryBasic
>     TestSkipList
>     TestText
>     TestTypes
>     """.split()
>
>     def test_suite():
>         return TestSuite(
>             [defaultTestLoader.loadTestsFromName(name, 
> repository.tests)
>                 for name in testNames]
>         )
>
> Of course, the names in 'testNames' could name individual test 
> classes, methods, or suite-generating functions.  They can also be 
> absolute module names instead of ones relative to a particular 
> location.
>
> So as you can see, there are a lot of potential ways we could organize 
> this, with different tradeoffs depending on what tests people want to 
> run and how often, not to mention how often they need to change the 
> list of the tests.  There are even tradeoffs depending on how many 
> tests you put in a single module.
>
> Currently, I notice that most Chandler tests have only one test class 
> per module, and many of those actually only have one test method per 
> test case.  I'm guessing that this is a byproduct of how long tests 
> take to run, coupled with the fact that hardhat (unlike unittest.py) 
> doesn't allow selecting an individual test within a module to run.
>
> By contrast, using unittest.py to select and run individual tests, you 
> can actually include more tests in the same module, with no overhead 
> penalty for doing so.  This will be important as we add fast-running 
> unit tests, as each unit test tests only a very small piece of 
> functionality, and there are therefore usually a lot of them.  For 
> example, PyProtocols has only seven test modules, but these contain 
> hundreds of test methods; a typical run of those tests involves maybe 
> 320 individual test methods, each with an average of maybe half a 
> dozen assertions being tested.
>
> So, if we start adding unit tests (in the strict isolationist sense) 
> to increase the coverage-to-runtime ratio of Chandler's tests, it's 
> likely that we'll tend towards putting more tests per class and more 
> classes per module than is currently done.  So, if we set up a 
> test-gathering strategy that works well for the current situation, it 
> won't necessarily continue to work long-term.
>
> On the other hand, I don't want to burden anybody with a 
> test-gathering style that would work well in the future, but which 
> might seem tedious for today's more limited needs.  So, it seems like 
> the best thing for me to do is throw all this out here for discussion 
> and find out what approach the team would prefer to take.
>
> (P.S. I forgot to mention...  right now 'application.tests' isn't a 
> package, so it needs an '__init__.py' if you want to use 'unittest.py' 
> to run any of the tests in it.    I think all the other test locations 
> are already packages.)
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> Open Source Applications Foundation "Dev" mailing list
> http://lists.osafoundation.org/mailman/listinfo/dev
>
----
Ted Leung                 Open Source Applications Foundation (OSAF)
PGP Fingerprint: 1003 7870 251F FA71 A59A  CEE3 BEBA 2B87 F5FC 4B42



More information about the Dev mailing list