views:

88

answers:

3

I have a class like the following:

class Positive(object):
    def __init__(self, item):
        self._validate_item(item)
        self.item = item

    def _validate_item(self, item):
        if item <= 0:
            raise ValueError("item should be positive.")

I'd like to write a unit test for _validate_item(), like the following:

class PositiveTests(unittest.TestCase):
    def test_validate_item_error(self):
        self.assertRaises(
            ValueError,
            Positive._validate_item,
            0
        )

Unfortunately, this won't work because the unit test only passes 0 to the method, instead of a class instance (for the self parameter) and the 0. Is there any solution to this other than having to test this validation method indirectly via the __init__() of the class?

+1  A: 

You're not creating an instance of Positive. How about

Positive()._validate_item, 0
djna
This code raises a TypeError because `Positive.__init__()` is missing the `item` argument.
Mike Mazur
This shows you you are trying to test the implementation "_validate_item", not the interface. I think testing `Positive(0)` is *much* better than testing `_validate_item(0)`.
kaizer.se
Above comment is to @gotgenes
kaizer.se
+7  A: 

If you're not using self in the method's body, it's a hint that it might not need to be a class member. You can either move the _validate_item function into module scope:

def _validate_item(item):
    if item <= 0:
        raise ValueError("item should be positive.")

Or if it really has to stay in the class, the mark the method static:

class Positive(object):
    def __init__(self, item):
        self._validate_item(item)
        self.item = item

    @staticmethod
    def _validate_item(item):
        if item <= 0:
            raise ValueError("item should be positive.")

Your test should then work as written.

Andre Stechert
I like this answer. If you feel the need to write tests for _validate_item() on its own, chances are it actually belongs on its own.
Mike Mazur
staticmethods are mostly pointless in Python, in that case use a classmethod!
kaizer.se
I wouldn't use the `@classmethod` decorator here for the same reason I wouldn't define it as an instance method. There's no use of `cls`. Given my druthers, I'd make it a private function at module scope.
Andre Stechert
+1  A: 

Well, _validate_item() is tested through the constructor. Invoking it with a null or negative value will raise the ValueError exception.

Taking a step back, that's the goal no ? The "requirement" is that object shall not be created with a zero or negative value.

Now this is a contrived example, so the above could not be applicable to the real class ; another possibility, to really have a test dedicated to the _validate_item() method, could be to create an object with a positive value, and then to invoke the validate_item() on it.

philippe