liori
liori

Reputation: 42367

Unit-test a method wrapper

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

Answers (2)

Mark Hildreth
Mark Hildreth

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

Martijn Pieters
Martijn Pieters

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

Related Questions