3

I've run into an issue writing unit tests for class attributes with the @property decorator. I am using the excellent py.test package for testing and would definitely prefer sticking with it because it's easy to set up fixtures. The code for which I'm writing unit tests looks something like this:

    class Foo(object):

        def __init__(self, fizz, buzz, (and many more...)):
            self.fizz = fizz
            self.buzz = buzz
            self.is_fizzing = #some function of fizz
            self.is_fizz_and_buzz = fizz and buzz
            ...

        @property
        def is_bar(self):
            return self.is_fizz_and_buzz and self.buzz > 5

        @property
        def foo_bar(self):
            # Note that this property uses the property above
            return self.is_bar + self.fizz

        # Long list of properties that call each other

The problem occurs when writing a unit test for a property which uses multiple other properties sometimes in long chains of up to four properties. For each unit test, I need to determine which inputs I need to set up and, even worse, it may be the case that those inputs are irrelevant for testing the functionality of that particular method. As a result, I end up testing many more cases than necessary for some of these "properties."

My intuition tells me that if these were actually "properties" they shouldn't have such long, involved calculations that need to be tested. Perhaps it would be better to separate out the actual methods (making them class methods) and write new properties which call these class methods.

Another issue with the current code--correct me if I'm wrong--is that every time a property is called (and most properties are called a lot) the attribute is recalculated. This seems terribly inefficient and could be fixed like this with new properties.

Does it even make sense to test properties? In other words, should an attribute itself be calculated in a property or should it only be set? Rewriting the code as I described above seems so unpythonic. Why even have properties in the first place? If properties are supposed to be so simple to not require testing, why not just define the attribute in the init?

Sorry if these are silly questions. I'm still fairly new to python.

Edit/update: Would it be possible (easy/pythonic) to mock the object and then perform the test on the property/attribute?

Community
  • 1
  • 1
Petergavinkin
  • 150
  • 11
  • What's un-Pythonic about it? – zeantsoi Jul 30 '16 at 02:05
  • Having a method that's called only once in one property for the purpose of testing seems like it may become overly complicated especially if I do that for all ~20 properties in a class, but you do have a point. It would simplify the testing a lot so may be worthwhile.. – Petergavinkin Aug 02 '16 at 23:53

1 Answers1

0

The easiest thing to do imo is to just test the property, e.g. foo_bar, as a normal method using it's function member, e.g., foo_bar.func, which @property automatically provides.

For me, using pytest & unittest.mock, this means instead of doing the below when foo_bar is a normal non-property method:

class TestFoo:
    def test_foo_bar(self):
        foo_mock = mock.create_autospec(Foo)
        # setup `foo_mock` however is necessary for testing
        assert Foo.foo_bar(foo_mock) == some_value

I would change the last line to do this:

        assert Foo.foo_bar.func(foo_mock) == some_value

If you need to stub/mock other properties in the process (like Foo.is_bar), take a look at unittest.mock.PropertyMock.

CrepeGoat
  • 2,315
  • 20
  • 24