Reputation: 42367
I've got a set of actions which share some base checks. My code looks like this:
def is_valid(param): …some pretty complex things unit-tested on their own…
class BaseAction(object):
def run(self, param):
if not is_valid(param):
raise ValueError(…)
return self.do_run(param)
class Jump(BaseAction):
def do_run(self, param): …
class Sing(BaseAction):
def do_run(self, param): …
How should I unit-test the fact that BaseAction.run performs validation?
I think I could unit-test Jump.run and Sing.run, but then to do it properly I'd need to write tests for each subclass of BaseAction, potentially quite a lot of cases. Also this means coupling test for subclasses with a test for base class' method.
Upvotes: 1
Views: 1813
Reputation: 43111
If you want to keep the logic in your Base class, see Martijn Pieters answer.
However, one possibility would be to restructure your code to have the validation occur in a function/class that ISN'T part of the Base class. Maybe the class that actually runs the actions itself can check those...
class Jump(object):
def run(self, param):
...
def run_action(action, param):
if not is_valid(param):
raise ValueError(…)
return action.run(param)
Unit testing actions is easier (you don't need to worry about the base logic), and it also won't break all your "Jump" unit tests if the "BaseAction" is somehow broken. "run_action" (you can make that a class instead of a function if you'd like) itself can be pretty easily tested (you can create a fake "Action" object).
Upvotes: 1
Reputation: 1124378
You would unit-test the is_valid()
function itself. You then only have to test for the fact that .run()
raises a ValueError
exception when passed invalid input:
with self.assertRaises(ValueError):
objectundertest.run(invalid_parameter)
Since you already have a unittest for is_valid()
itself, your unittests for .run()
do not need to focus on it's functionality. You can focus on the functionality unique to .run()
.
In your case BaseAction
is a unit providing a service to subclasses; I'd test this as a mock subclass:
class MockAction(BaseAction):
run_called = None
def do_run(self, param):
self.run_called = param
so you can check if run
indeed called do_run
with the expected param
value.
Upvotes: 2