Reputation: 13135
I use the following function to add tests to my Test Cases.
def add_tests(cls, args, name_builder):
for a in args:
def tb(a):
return lambda self: self.test_body(*a)
setattr(cls, name_builder(a), tb(a))
At the moment I can use it like this:
class TestEqual(unittest.TestCase):
def test_body(self, i, j):
self.assertNotEquals(0, i-j)
add_tests(TestEqual, ((0,0),(1,1)), build_name_1)
How can I change add_tests in order to do something like this:
class TestRestart(unittest.TestCase):
def test_mode_flow(self, mode):
machine.set_mode(mode)
self.assertTrue(machine.restart())
def test_gear_flow(self, gear, speed):
machine.set_gear(gear)
machine.set_speed(speed)
self.assertTrue(machine.restart())
add_tests(TestRestart, test_mode_flow, (mode.SILENT, mode.NOISY), build_name_mode)
add_tests(TestRestart, test_gear_flow, ((1,33),(4,22)), build_name_gear)
In other words, I want to remove the hardcoded call to self.test_body(*a)
and instead pass in a method of choice as an argument.
Upvotes: 0
Views: 38
Reputation: 101959
You can pass the name of the method to your add_tests
function:
def add_tests(cls, method_name, name_builder, *args):
method = getattr(cls, method_name)
for arg in args:
setattr(cls, name_builder(arg), lambda self, arg=arg: method(*arg))
Used as:
class TestRestart(unittest.TestCase):
def test_mode_flow(self, mode):
machine.set_mode(mode)
self.assertTrue(machine.restart())
def test_gear_flow(self, gear, speed):
machine.set_gear(gear)
machine.set_speed(speed)
self.assertTrue(machine.restart())
add_tests(TestRestart, 'test_mode_flow', build_name_mode, mode.SILENT, mode.NOISY)
add_tests(TestRestart, 'test_gear_flow', build_name_gear, (1,33), (4,22))
An alternative is to pass the method directly:
def add_tests(cls, method, name_builder, *args):
for arg in args:
setattr(cls, name_builder(arg), lambda self, arg=arg: method(self, *arg))
And use it as:
add_tests(TestRestart, TestRestart.test_mode_flow, build_name_mode, mode.SILENT, mode.NOISY)
add_tests(TestRestart, TestRestart.test_gear_flow, build_name_gear, (1,33), (4,22))
An other option would be to use a decorator:
from functools import wraps
def add_tests(*args):
def decorator(method):
@wraps(method)
def wrapper(self):
for arg in args:
method(self, *arg)
return wrapper
return decorator
and use it as:
class TestRestart(unittest.TestCase):
@add_tests(mode.SILENT, mode.NOISY)
def test_mode_flow(self, mode):
machine.set_mode(mode)
self.assertTrue(machine.restart())
@add_tests((1, 33), (4, 22))
def test_gear_flow(self, gear, speed):
machine.set_gear(gear)
machine.set_speed(speed)
self.assertTrue(machine.restart())
However this packs all tests into a single method, which may not be what you want.
However it's possible to modify add_tests
so that the error message show which argument failed, thus providing the same level of information.
Upvotes: 1