Reputation: 18264
I have the following test class defined. It uses exec which generally I dislike.
class FubarTest(unittest.TestCase):
lst = [(True, True),
(False, False)]
for t in lst:
function = """def test_{}_is_{}(self):
self.assertTrue({} is {})
""".format(t[0], t[1], t[0], t[1])
exec function
When I run it (via py.test but that should not matter), I get this:
============================= test session starts ==============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /usr/bin/python
plugins: capturelog, cov, twisted, xdist
model/test/test_hframe5.py <- <string>:1: HFrame5Test.test_False_is_False PASSED
model/test/test_hframe5.py <- <string>:1: HFrame5Test.test_True_is_True PASSED
=========================== 2 passed in 0.49 seconds ===========================
So that the test are automatically created and have sensible names so if something fails, you know by the name of the method what has failed.
Clearly this is a much simplified example of what I want to do. I have a lot of test that will look like "do X, check that Y is True and Z is False" which could be easily coded with the above method. I could write three dozen copy-and-pasted methods but that just feels wrong -- breaking DRY.
Is there a more pythonic way of writing this code?
Upvotes: 3
Views: 163
Reputation: 19154
If you use nose, you can use a test generator:
class FubarTest(object):
def check_is(self, a, b):
assert {} is {}
def test_is(self):
lst = [(True, True), (False, False)]
for a, b in lst:
self.check_is(a, b)
Upvotes: 1
Reputation: 157444
You want parametrized tests. For example, with parameterizedtestcase:
from parameterizedtestcase import ParameterizedTestCase
class MyTests(ParameterizedTestCase):
@ParameterizedTestCase.parameterize(
("value", "expected"),
[
(True, True),
(False, False),
]
)
def test_identical(self, value, expected):
self.assertTrue(value is expected)
Using py.test's parametrization framework:
import pytest
@pytest.mark.parametrize(
("value", "expected"),
[
(True, True),
(False, False),
]
)
def test_identical(value, expected):
assert value is expected
Upvotes: 1
Reputation: 95722
You almost certainly don't need to use exec
. Just use 'setattr' to add a suitable function to the class, and use a nested scope to bind the test values. For example, I think this should work:
class FubarTest(unittest.TestCase):
pass
def createTest(cls, arg1, arg2):
def test(self):
self.assertTrue(arg1 is arg2)
testname = "test_{}_is_{}".format(arg1, arg2)
setattr(cls, testname, test)
def createTests():
lst = [(True, True),
(False, False)]
for a,b in lst:
createTest(FubarTest, a, b)
createTests()
Upvotes: 1
Reputation: 13279
I would usually err on the side of more test than less test, really no matter how it gets done. You're probably fine.
Upvotes: 1