zchtodd
zchtodd

Reputation: 1150

Passing python script arguments to test modules

I have several test modules that are all invoked together via a driver script that can take a variety of arguments. The tests themselves are written using the python unittest module.

import optparse
import unittest
import sys
import os

from tests import testvalidator
from tests import testmodifier
from tests import testimporter

#modify the path so that the test modules under /tests have access to the project root
sys.path.insert(0, os.path.dirname(__file__))

def run(verbosity):
    if verbosity == "0":
            sys.stdout = open(os.devnull, 'w')

    test_suite = unittest.TestSuite()
    test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(testvalidator.TestValidator))
    test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(testmodifier.TestModifier))
    test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(testimporter.TestDataImporter))

    unittest.TextTestRunner(verbosity=int(verbosity)).run(test_suite)

if __name__ == "__main__":

    #a simple way to control output verbosity
    parser = optparse.OptionParser()
    parser.add_option("--verbosity", "--verbosity", dest="verbosity", default="0")
    (options, args) = parser.parse_args()

    run(options.verbosity)

My issue is that, within these test modules, I have certain tests I'd like to skip based on different parameters passed to the driver. I'm aware that unittest provides a family of decorators meant to do this, but I don't know the best way to pass this information on to the individual modules. If I had a --skip-slow argument, for example, how could I then annotate tests as slow, and have them skipped?

Thank you for your time.

Upvotes: 6

Views: 1808

Answers (3)

Jim DeLaHunt
Jim DeLaHunt

Reputation: 11415

Here's how I solved this problem. At the bottom of my module, I put this code to set a global variable based on the presence of a --slow argument in argv:

if __name__ == "__main__":
    try:
        i = sys.argv.index("--slow")
        run_slow_tests=True
        del sys.argv[i]
    except ValueError:
        pass

    unittest.main()

Then at the beginning of test functions which would be slow to run, I put this statement. It raises the unittest.SkipTest() exception if the flag isn't set to include slow tests.

if not run_slow_tests:
    raise unittest.SkipTest('Slow test skipped, unless --slow given in sys.argv.')

Then when I invoke the module normally, the slow tests are skipped.

% python src/my_test.py -v       
test_slow (__main__.Tests) ... skipped 'Slow test skipped, unless --slow given in sys.argv.'

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK (skipped=1)

And when I add the --slow, the slow tests in that module run:

% python src/my_test.py -v --slow
test_slow (__main__.Tests) ... ok

----------------------------------------------------------------------
Ran 1 test in 10.110s

OK

Unfortunately, this doesn't work with Unittest's test discovery.

% python -m unittest discover src "*_test.py" --slow
usage: python -m unittest discover [-h] [-v] [-q] [--locals] [-f] [-c] [-b]
                                   [-k TESTNAMEPATTERNS] [-s START]
                                   [-p PATTERN] [-t TOP]
python -m unittest discover: error: unrecognized arguments: --slow

It also didn't work to use the @unittest.SkipUnless() decorator. I suspect this is because the decorator evaluates its arguments at module definition time, but the argument isn't set to the correct value until module run time, which is later.

It isn't perfect, but it lets me work within the Python standard library. A requirement like this is a good reason to adopt a better framework, such as nose tests. For my current project, I prefer to avoid installing any outside modules.

Upvotes: 0

John Doe
John Doe

Reputation: 3506

I had in fact been wondering this myself, and finally found the solution.

main file...

...
if __name__ == '__main__':
    args = argparser()

    from tests import *

    ...

And in your test modules, just do:

from __main__ import args

print args

I tested this out, and it worked rather nicely. Nice thing is how simple it is, and it's not too much of a hack at all.

Upvotes: 2

jcollado
jcollado

Reputation: 40414

You can use nose test runner with the attrib plugin that lets you select test cases based on attributes. In particular, the example in the plugin documentation uses @attr(slow) to mark slow test cases.

After that, from the command line:

  • To select all the test cases marked as slow:

    $ nosetests -a slow

  • To select all the test cases not marked as slow:

    $ nosetests -a '!slow'

Upvotes: 2

Related Questions