27

I just found that a bunch of unit tests are failing, due a developer hasn't mocked out the dependency to a redis client within the test. I'm trying to give a hand in this matter but have difficulties myself.

The method writes to a redis client:

redis_client = get_redis_client()
redis_client.set('temp-facility-data', cPickle.dumps(df))

Later in the assert the result is retrieved:

res = cPickle.loads(get_redis_client().get('temp-facility-data'))
expected = pd.Series([set([1, 2, 3, 4, 5])], index=[1])
assert_series_equal(res.variation_pks, expected)

I managed to patch the redis client's get() and set() successfully.

@mock.patch('redis.StrictRedis.get')
@mock.patch('redis.StrictRedis.set')
def test_identical(self, mock_redis_set, mock_redis_get):
    mock_redis_get.return_value = ???
    f2 = deepcopy(self.f)
    f3 = deepcopy(self.f)
    f2.pk = 2
    f3.pk = 3
    self.one_row(f2, f3)

but I don't know how to set the return_value of get() to what the set() would set in the code, so that the test would pass.

Right now this line fails the test:

res = cPickle.loads(get_redis_client().get('temp-facility-data'))
TypeError: must be string, not MagicMock

Any advice please?

Houman
  • 64,245
  • 87
  • 278
  • 460
  • use `side_effect` with a shared object being set in the set `side_effect` and returned in get `side_effect`? https://docs.python.org/3/library/unittest.mock.html#quick-guide – user3012759 May 29 '15 at 14:24

3 Answers3

27

Think you can use side effect to set and get value in a local dict

data = {}
def set(key, val):
    data[key] = val

def get(key):
    return data[key]

mock_redis_set.side_effect = set
mock_redis_get.side_effect = get

not tested this but I think it should do what you want

user3012759
  • 1,977
  • 19
  • 22
  • 2
    Can you give a little bit more context? (write a complete test, with mock creation, actual get/set usage in a code that you test). Also, how do you prevent connection from crashing, I guess that even if you mock get/set, pool connections (and any other network related connection) will still crash. – cglacet Aug 09 '22 at 15:02
  • 1
    Thank you for sharing. I found out that using get is better since using a non-existing key on REDIS does not raise exception. ``` def get(key): return data.get(key) ``` – Ajite Dec 14 '22 at 01:54
7

If you want something more complete, you can try fakeredis

@patch("redis.Redis", return_value=fakeredis.FakeStrictRedis())
def test_something():
....
Narek
  • 548
  • 6
  • 26
1

I think you can do something like this.

redis_cache = {
    "key1": (b'\x80\x04\x95\x08\x00\x00\x00\x00\x00\x00\x00\x8c\x04test\x94.', "test"),
    "key2": (None, None),
}
    
def get(redis_key):
    if redis_key in redis_cache:
        return redis_cache[redis_key][0]
    else:
        return None
    
mock = MagicMock()
mock.get = Mock(side_effect=get)
    
with patch('redis.StrictRedis', return_value=mock) as p:
    for key in redis_cache:
        result = self.MyClass.my_function(key)
        self.assertEqual(result, redis_cache[key][1])
Akarshan
  • 21
  • 1