[Tutor] Test code organization

Kent Johnson kent37 at tds.net
Sun Mar 12 04:13:01 CET 2006


Willi Richert wrote:
> Hi,
> 
> for some time I try to find the best test code organization. I've come 
> up with the following solution, which I guess is not optimal. Please 
> comment.

OK I'll take a stab at it.
> 
> In the code directory there is a special tests directory, which contains 
> all the unit test files. 

This is a common organization but I find I prefer to put the tests in 
the same directory as the modules under test. It makes the directory a 
bit more crowded but it keeps the modules and their tests close together.

> There is the file alltests.py which collects 
> all python test files and executes them:
> 
> =====================================
> import sys, unittest
> sys.path.append(".")
> sys.path.append("..")
> 
> TEST_DIR = "tests"
> 
> import glob
> testCases = [t.split(".")[0] for t in glob.glob1(TEST_DIR, "*Tests.py")]
> print "Found test case modules "+", ".join(testCases)
> print
> 
> for t in testCases:
>         exec("from %s import %s"%(TEST_DIR, t))

You could probably replace this with __import__ (or my_import() from the 
doc page for __import__()) but maybe it's not worth the trouble.
> 
> def suite():
>     exec("suites = tuple([x.suite() for x in [%s]])"%str(", 
> ".join(testCases)))

I don't think you need exec here, you could use
   suites = [sys.modules.get(x).suite() for x in testCases]

>     suite = unittest.TestSuite(suites)
>     return suite
> 
> 
> if __name__ == '__main__':
>         suite = suite()
>         result = unittest.TextTestRunner(verbosity=2).run(suite)
>         if result.wasSuccessful():
>                 sys.exit(0)
>         else:
>                 sys.exit(1)
> ======================================
> 
> 
> For every class to test I create a ClassTests.py file which contains the 
> following code:
> Header:
> ======================================
> import sys
> sys.path.append("..")
> 
> import unittest
> from Class... import *
> ======================================
> 
> The footer parses the available test classes:
> ======================================
> def _testclasses():
>     mod = sys.modules[__name__]
>     return [getattr(mod, name) for name in dir(mod) if 
> name.endswith('TestCase')]
> 
> def suite():
>         return unittest.TestSuite([unittest.makeSuite(tc) for tc in 
> _testclasses()])

I think all the above footer could be replaced with
def suite():
   return 
unittest.defaultTestLoader.loadTestsFromModule(sys.modules.get(__name__))

> 
> if __name__ == '__main__':
>                 unittest.main()
> ======================================
> 
> 
> What smalls badly is the sys.path.append() stuff every test file must 
> have. Improvements?

I usually run everything from the base directory of the project. On 
Windows, the current dir is added to sys.path automatically. (On linux 
apparently this is not true.) Then the test classes can use normal 
imports and there is no need to alter sys.path in each test class.

You might want to look at some of the other auto-discovery methods such 
as those included in nose and py.test, either for your own use or to see 
how they work.
http://somethingaboutorange.com/mrl/projects/nose/
http://codespeak.net/py/current/doc/test.html

Kent



More information about the Tutor mailing list