Suppose we would like to write test cases for a (simple) math library that (currently) only implements two routines sqrt
and ln
. Among other things, both routines are expected to raise a ValueError
if their input is negative. So, the test cases would look like this:
import unittest
from my_math_lib import sqrt, ln
class TestSqrt(unittest.TestCase):
def test_negatives(self):
self.assertRaises(ValueError, sqrt, -5)
# ... any many other cases
class TestLn(unittest.TestCase):
def test_negatives(self):
self.assertRaises(ValueError, ln, -5)
# ... any many other cases
Obviously the two test cases TestSqrt.test_negatives
and TestLn.test_negatives
are identical except for the function that is being tested, and repeating exactly the same code is a bad idea.
Is there away to auto-generate such test cases? For example, in C/C++ one could have implemented a macro that defines the test code with the appropriate function name. Is there a similar solution in Python?
There is the possibility to subclass as proposed by @jonrsharpe, though that has one caveat. You cannot derive your base class from unittest.TestCase
and write a testXXX
there, because that also would be found and executed, and would fail. You need a mix-in instead:
class BaseTest:
def setUp(self):
self.tested = None
def test_negatives(self):
self.assertRaises(ValueError, self.tested, -5)
class TestSqrt(unittest.TestCase, BaseTest):
def setUp(self):
self.tested = sqrt
class TestLn(unittest.TestCase, BaseTest):
def setUp(self):
self.tested = log
This works, but is not nice because the base class calls a method from TestCase
( assertRaises
) that it is not derived from, so every linter or IDE check will warn you about this.
To work around that problem, you could just use one of the real implementations as the base class:
class TestSqrt(unittest.TestCase):
def setUp(self):
self.tested = sqrt
def test_negatives(self):
self.assertRaises(ValueError, self.tested, -5)
class TestLn(TestSqrt):
def setUp(self):
self.tested = log
class TestSomethingElse(TestSqrt):
def setUp(self):
self.tested = something_else
What I would use instead is fixture parametrization, if you can use pytest:
import pytest
@pytest.fixture(params=[sqrt, log])
def tested_function(request):
return request.param
def test_negatives(tested_function):
with pytest.raises(ValueError):
tested_function(-5)
def test_something_else(tested_function):
...
You could also put that into a class, of course.
Caveat: this does not work with unittest
, or at least I don't know a clean way to do it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.