views:

538

answers:

5

The very common directory structure for even a simple Python module seems to be to separate the unit tests into their own test directory:

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

for example see this Python project howto.

My question is simply What's the usual way of actually running the tests? I suspect this is obvious to everyone except me, but you can't just run python test_antigravity.py from the test directory as its import antigravity will fail as the module is not on the path.

I know I could modify PYTHONPATH and other search path related tricks, but I can't believe that's the simplest way - it's fine if you're the developer but not realistic to expect your users to use if they just want to check the tests are passing.

The other alternative is just to copy the test file into the other directory, but it seems a bit dumb and misses the point of having them in a separate directory to start with.

So, if you had just downloaded the source to my new project how would you run the unit tests? I'd prefer an answer that would let me say to my users: "To run the unit tests do X."

A: 

Use setup.py develop to make your working directory be part of the installed Python environment, then run the tests.

Ned Batchelder
This gets me an `invalid command 'develop'` and this option isn't mentioned if I ask for `setup.py --help-commands`. Does there need to be something in the `setup.py` itself for this to work?
Major Major
It's OK - the problem was I was missing an `import setuptools` from my `setup.py` file. But I guess that does go to show that this won't work all the time for other people's modules.
Major Major
+3  A: 

From the article you linked to:

Create a test_modulename.py file and put your unittest tests in it. Since the test modules are in a separate directory from your code, you may need to add your module’s parent directory to your PYTHONPATH in order to run them:

$ cd /path/to/googlemaps

$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps

$ python test/test_googlemaps.py

Finally, there is one more popular unit testing framework for Python (it’s that important!), nose. nose helps simplify and extend the builtin unittest framework (it can, for example, automagically find your test code and setup your PYTHONPATH for you), but it is not included with the standard Python distribution.

Perhaps you should look at nose as it suggests?

Mark Byers
+1 for nose. It works very well.
Virgil Dupras
Yes this works (for me), but I'm really asking for the simplest instructions that I can give users to my module to get them to run the tests. Modifying the path might actually be it, but I'm fishing for something more straight-forward.
Major Major
A: 

if you run "python setup.py develop" then the package will be in the path. But you may not want to do that because you could infect your system python installation, which is why tools like virtualenv and buildout exist.

Tom Willis
+6  A: 

The simplest solution for your users (not necessarily for you) is to provide an executable script (runtests.py or some such) which bootstraps the necessary test environment, including adding your root project directory to sys.path temporarily. This doesn't have to involve users setting environment variables, something like this works fine in a bootstrap script:

import sys, os

sys.path.insert(0, os.path.dirname(__file__))

Then your instructions to your users can be as simple as "python runtests.py".

Carl Meyer
`import os` too
Vince
+2  A: 

I generally create a "run tests" script in the project directory (the one that is common to both the source directory and test) that loads my "All Tests" suite. This is usually boilerplate code, so I can reuse it from project to project.

run_tests.py:

import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)

test/all_tests.py (from http://stackoverflow.com/questions/1732438)

import glob
import unittest

def create_test_suite():
    test_file_strings = glob.glob('test/test_*.py')
    module_strings = ['test.'+str[5:len(str)-3] for str in test_file_strings]
    suites = [unittest.defaultTestLoader.loadTestsFromName(name) \
              for name in module_strings]
    testSuite = unittest.TestSuite(suites)
    return testSuite

With this setup, you can indeed just include antigravity in your test modules. The downside is you would need more support code to execute a particular test... I just run them all every time.

stw_dev