Instantiate Python unittest.TestCase with arguments
Asked Answered
F

3

18

I would like to iterate over a list of items, and run an assertion on each of them. One example might be checking whether each number in a list is odd.

TestCase:

class TestOdd(unittest.TestCase):
    def runTest(self):
        """Assert that the item is odd"""
        self.assertTrue( NUMBER %2==1, "Number should be odd")

Test suite:

if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(TestOdd())
    # I would like to have:
    # suite.addTest(TestOdd(1))
    # suite.addTest(TestOdd(2))
    # suite.addTest(TestOdd(3))
    # ...
    unittest.main()

How can I instantiate a TestOdd object with an argument - for example, the number to be tested?

Update: According to a blog post from 2011 (posted as answer), there is no built-in mechanism for parametrized tests. I will be happy to accept any cleaner solutions.

Femi answered 23/6, 2013 at 11:50 Comment(1)
See also: Accessing argparse arguments from the class at CRBelittle
R
10

Same can be achieved using class attributes.

class TestOdd1(unittest.TestCase):
    NUMBER=1
    def runTest(self):
        """Assert that the item is odd"""
        self.assertTrue( self.NUMBER % 2 == 1, "Number should be odd")

class TestOdd2(TestOdd1):
    NUMBER=2

if __name__ == '__main__':
    unittest.main()

The unittesting will discover them automatically, so no need to create a suite.

If you want to avoid using a TestCase for base class, you can use multiple inheritance:

from unittest import TestCase, main

class TestOdd:
    def runTest(self):
        """Assert that the item is odd"""
        self.assertTrue( self.NUMBER % 2 == 1, "Number should be odd")

class TestOdd1(TestOdd, TestCase):
    NUMBER=1
class TestOdd2(TestOdd, TestCase):
    NUMBER=2

if __name__ == '__main__':
    main()
Ricker answered 19/3, 2014 at 20:40 Comment(0)
F
14

According to "Python unit testing: parametrized test cases", published in Eli Bendersky's blog:

Python’s standard unittest library is great and I use it all the time. One thing missing from it, however, is a simple way of running parametrized test cases. In other words, you can’t easily pass arguments into a unittest.TestCase from outside.

Eli's solution is inheriting unittest.TestCase into ParametrizedTestCase. I'm not sure about copyright issues, so I won't copy-paste the code here.

If there is any better solution, I will be happy to accept it.

Femi answered 23/6, 2013 at 12:2 Comment(2)
In 2018, Eli Bendersky's blog is still the best way to achieve this.Gastrin
This is interesting, but seems limited by the fact that you can't have more than one param per TestCase without changing the init signatureConcession
R
10

Same can be achieved using class attributes.

class TestOdd1(unittest.TestCase):
    NUMBER=1
    def runTest(self):
        """Assert that the item is odd"""
        self.assertTrue( self.NUMBER % 2 == 1, "Number should be odd")

class TestOdd2(TestOdd1):
    NUMBER=2

if __name__ == '__main__':
    unittest.main()

The unittesting will discover them automatically, so no need to create a suite.

If you want to avoid using a TestCase for base class, you can use multiple inheritance:

from unittest import TestCase, main

class TestOdd:
    def runTest(self):
        """Assert that the item is odd"""
        self.assertTrue( self.NUMBER % 2 == 1, "Number should be odd")

class TestOdd1(TestOdd, TestCase):
    NUMBER=1
class TestOdd2(TestOdd, TestCase):
    NUMBER=2

if __name__ == '__main__':
    main()
Ricker answered 19/3, 2014 at 20:40 Comment(0)
F
0

I would go this way:

TestCase file containing test logic itself:

import my_config
from unittest import TestCase

class TestOdd(unittest.TestCase):
    def runTest(self):
        """Assert that the item is odd"""
        self.assertTrue(int(my_config.options['number']) %2==1, "Number should be odd")

then in the TestSuite file:

import argparse
import my_config
import TestCase

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-num', '--number', type=int, default=0)
    my_config.options = parser.parse_args()
    
    suite_case = unittest.TestLoader().loadTestsFromTestCase(TestCase)
    test_suite = unittest.TestSuite([suite_case])

    unittest.TextTestRunner(verbosity=1, failfast=True, buffer=False).run(test_suite)

my_config.py helping file:

options = {}

and then from command line we can execute:

python3 -m unittest TestSuite.py --number=1
python3 -m unittest TestSuite.py --number=2
.
.
python3 -m unittest TestSuite.py --number=1000

Or execution calling from command line could be done by using for cycle inside Shell script.

Fluff answered 3/12, 2020 at 19:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.