nimiq
nimiq

Reputation: 191

How to use mock to test a getter used for caching?

In the following class the property wheels has a cached value.

import time
class Car:
    @property
    def wheels(self):
        if not hasattr(self, '_wheels'):
            self._count_wheels()
        return self._wheels

    def _count_wheels(self):
        time.sleep(10)  # simulate a long calculation
        self._wheels = 4


if __name__ == "__main__":
    c = Car()
    print(c.wheels)  # calls _count_wheels() once
    print(c.wheels)  # no calls to _count_wheels()

I want to test that the first call to c.wheels calls once the method _count_wheels(); while the second call to c.wheels doesn't call the method _count_wheels()
I'd like to use unittest.mock

Upvotes: 3

Views: 1215

Answers (1)

Alvaro Fuentes
Alvaro Fuentes

Reputation: 17485

One simple solution is to mock the object yourself:

if __name__ == "__main__":

    count = 0    
    to_mock = Car._count_wheels
    def mocked(self):
        global count
        count +=1
        if count>1:
            raise ValueError("Called twice")
        to_mock(self)        

    Car._count_wheels = mocked
    c = Car()
    print(c.wheels)  # calls _count_wheels() once
    try:
        print(c.wheels)  # no calls to _count_wheels()
    except ValueError as e:
        print e

You can try it with this modified Car class:

class Car:
    @property
    def wheels(self):
        #if not hasattr(self, '_wheels'):
        self._count_wheels()
        return self._wheels

    def _count_wheels(self):
        #time.sleep(10)  # simulate a long calculation
        self._wheels = 4

And you will see it raises the exception. Since python is so dynamic this approach is always valid and sometimes is very useful, but of course you can use a third party tool as well ;)

Upvotes: 1

Related Questions