4
\$\begingroup\$

I have the following sample code running unit tests:

#!/usr/bin/env python3

import sys
import unittest
import argparse

class ParentTest(unittest.TestCase):
    None

class Test1(ParentTest):
    def test_if_verbose(self):
        import __main__ # FIXME
        print("Success!") if __main__.args.verbose else "" # FIXME

class Test2(ParentTest):
    None

if __name__ == '__main__':
    # Parse arguments.
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("-?", "--help",     action="help",                        help="show this help message and exit" )
    parser.add_argument("-v", "--verbose",  action="store_true", dest="verbose",  help="increase output verbosity" )
    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    print(args.verbose)

    # Add tests.
    alltests = unittest.TestSuite()
    alltests.addTest(unittest.makeSuite(Test1))
    alltests.addTest(unittest.makeSuite(Test2))

    result = unittest.TextTestRunner(verbosity=2).run(alltests) # Run tests.
    sys.exit(not result.wasSuccessful())

and I'd like to access command-line argument values from the class.

In here it's suggested that using __main__ isn't really a good approach.

Is there any better way of doing it, where individual tests could have access to argument values which were parsed already in main?

Testing:

python3 test.py
python3 test.py -v
\$\endgroup\$
4
\$\begingroup\$

You could do this by making the test classes take args as parameter, and crafting a custom make_suite method instead of unittest.makeSuite, like this:

class ParentTest(unittest.TestCase):
    def __init__(self, methodName='runTest', args=None):
        super().__init__(methodName)
        self.args = args

class Test1(ParentTest):
    def test_if_verbose(self):
        print("Success!") if self.args.verbose else ""

class Test2(ParentTest):
    pass

if __name__ == '__main__':
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("-?", "--help",     action="help",                        help="show this help message and exit" )
    parser.add_argument("-v", "--verbose",  action="store_true", dest="verbose",  help="increase output verbosity" )
    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    print(args.verbose)

    def make_suite(testcase_class):
        testloader = unittest.TestLoader()
        testnames = testloader.getTestCaseNames(testcase_class)
        suite = unittest.TestSuite()
        for name in testnames:
            suite.addTest(testcase_class(name, args=args))
        return suite

    # Add tests.
    alltests = unittest.TestSuite()
    alltests.addTest(make_suite(Test1))
    alltests.addTest(make_suite(Test2))

    result = unittest.TextTestRunner(verbosity=2).run(alltests) # Run tests.
    sys.exit(not result.wasSuccessful())

The sample output is the same as the original code:

$ python t2.py ; python t2.py -v
False
test_if_verbose (__main__.Test1) ... ok

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

OK
True
test_if_verbose (__main__.Test1) ... Success!
ok

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

OK

The code in my answer was inspired by these posts:

\$\endgroup\$
  • \$\begingroup\$ super()__init__ should be super(ParentTest,self)__init__? apart from this, this is a great example \$\endgroup\$ – Gang May 8 '16 at 17:17
1
\$\begingroup\$

Why not just access args directly as a global variable, instead of through __main__? If this is all just a single file -- which means it is one module called __main__ -- then you don't need to import anything. Instead of this:

def test_if_verbose(self):
    import __main__ # FIXME
    print("Success!") if __main__.args.verbose else "" # FIXME

Just do this:

def test_if_verbose(self):
    print("Success!") if args.verbose else ""

Of course, that will only work as long as you don't split the code into modules. janos's solution looks cleaner and doesn't have that restriction -- I just wanted to point out that in this particular case, technically, a simpler way exists.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.