3

Summary : I am doing an experiment to try to create a simple mock to replace redis. What I'm trying to do should be obvious from the code. Short version is, the mock doesn't work - It's still going to redis and creating keys.

tests.py:



from django.test import TestCase
import mock
from redis_mock.simple_redis_mock import redisMockGetRedis, redisMockFlushDB
from account.util import get_redis

class SimpleTest(TestCase):

    def setUp(self):
        redisMockFlushDB()

    @mock.patch("account.util.get_redis", redisMockGetRedis)
    def test_redis(self):
        key = "hello123"
        value = "world123"
        r = get_redis()
        r.set(key, value)
        value2 = r.get(key)
        self.assertEqual(value, value2)

util.py:

import redis

REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DEFAULT_DB = 0

def get_redis():
    print "account.util.get_redis"
    return redis.StrictRedis(
        REDIS_HOST, 
        REDIS_PORT, 
        REDIS_DEFAULT_DB
    )

simple_redis_mock.py:

"""
A simple mock for Redis. Just mocks set, get and expire commands.
"""

class SimpleRedisMockDB:
    db = {}

def redisMockFlushDB():
    """
    Helper function to flush the RedisMock db between test runs
    """
    print "redisMockFlushDB"
    SimpleRedisMock.db = {}

class SimpleRedisMock:
    def get(self, key):
        val = None
        try:
            val = SimpleRedisMockDB.db[key]
        except:
            pass
        print "SimpleRedisMock get(" + str(key) + "):" + str(val)
        return val

    def set(self, key, val):
        print "SimpleRedisMock set(" + str(key) + "," + str(val) +")"
        SimpleRedisMockDB.db[key] = val

    def expire(self, key):
        pass


def redisMockGetRedis():
    print "redisMockGetRedis"
    return SimpleRedisMock()

Now, what I expect is that when I run my test, no redis keys are set. Here's what actually happens:

twang$ redis-cli
redis 127.0.0.1:6379> del hello123
(integer) 1
redis 127.0.0.1:6379> exit
twang$ ./manage.py test account
Creating test database for alias 'default'...
redisMockFlushDB
account.util.get_redis
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Destroying test database for alias 'default'...
twang$ redis-cli
redis 127.0.0.1:6379> get hello123
"world123"

Simple question : Why isn't mock.patch doing what I expect?

user1855081
  • 31
  • 1
  • 2
  • Possible duplicate of [Python mock patch doesn't work as expected for public method](https://stackoverflow.com/questions/30987973/python-mock-patch-doesnt-work-as-expected-for-public-method) – funky-future May 24 '17 at 21:15

3 Answers3

12

The link

http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch

helped me understand better how to go about patching function calls in python.

Geoffrey
  • 141
  • 2
  • 3
8

Patch only mocks out the object in the location that you have patched it. A quick bit of example code should help explain what is going on.

from mock import Mock, patch
import unittest

from patch_code import anotherfunc, thefunc

print 'Function imported', thefunc

class SomeTest(unittest.TestCase):

    @patch('patch_code.thefunc', Mock())
    def test_method(self):
        anotherfunc()
        print 'Inside test method', thefunc


if __name__ == '__main__':
    unittest.main()

And the code under test is just two functions:

def thefunc():
    pass

def anotherfunc():
    print 'Inside code under test', thefunc

Running that test gives the following output

Function imported <function thefunc at 0xb740e614>
Inside code under test <Mock id='3071597132'>
Inside test method <function thefunc at 0xb740e614>

You can clearly see that the only place that patch has mocked 'thefunc' is in the code under test.

If you want to test the function get_redis without causing any side effects then you should mock out account.util.redis.StrictRedis and assert that it was called with the correct arguments.

If you want to test functions that use get_redis then you should mock out get_redis and, import the function that is using get_redis, and call that function in your test.

aychedee
  • 24,871
  • 8
  • 79
  • 83
3

Instead of importing get_redis, try import util and call util.get_redis().

Hamed Abdi
  • 31
  • 2