Mocked Redis counter does not increment between requests for different users
I am testing a Django view that increments recipe view counters using Redis.
The logic is:
each user can increment recipe views only once
different users should increment the counter
Redis is mocked using
MagicMock
The first test passes, and part of the second test passes, but the counter never increments to 2 for the second user, even though I explicitly change mock return values.
Django View
# recipehub/apps/recipes/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, render
from recipehub.apps.recipes.models import Recipe
from recipehub.redis import r
@login_required
def recipe_detail(request, slug):
recipe = get_object_or_404(Recipe, slug=slug, moderation_status="approved")
redis_user_recipe_view_key = f"user:{request.user.id}:recipe:{recipe.id}:view"
redis_all_recipe_view_key = f"recipe:{recipe.id}:views"
if not r.exists(redis_user_recipe_view_key):
r.set(redis_user_recipe_view_key, 1)
r.incr(redis_all_recipe_view_key)
recipe_views = int(r.get(redis_all_recipe_view_key) or 0)
return render(
request,
"recipes/recipe_detail.html",
{
"recipe": recipe,
"views": recipe_views,
},
)
Redis client definition
# recipehub/redis.py
import redis
r = redis.Redis(host="localhost", port=6379, db=0)
**
Redis mock fixture**
import pytest
from unittest.mock import MagicMock
@pytest.fixture()
def mock_redis(mocker):
magic_mock = MagicMock()
mocker.patch("recipehub.redis.r", magic_mock)
return magic_mock
Test 1 — first user increments counter (passes)
@pytest.mark.django_db
def test_recipe_detail_first_user_view_increments_counter(client, users_list, mock_redis):
recipe = RecipeFactory.create(slug="fish", moderation_status="approved")
user = users_list["first_simple_user"]
mock_redis.exists.return_value = False
mock_redis.get.return_value = b'1'
mock_redis.incr.return_value = 1
mock_redis.set.return_value = True
client.force_login(user)
response = client.get(
reverse("recipes:recipe-detail", kwargs={"slug": recipe.slug})
)
assertContains(response, '1 views')
Test 2 — different users should increment counter (fails)
@pytest.mark.django_db
def test_recipe_detail_different_users_increment_counter(client, users_list, mock_redis):
recipe = RecipeFactory.create(slug="fish", moderation_status="approved")
first_user = users_list["first_simple_user"]
second_user = users_list["second_simple_user"]
# First user visit
mock_redis.exists.return_value = False
mock_redis.get.return_value = b'1'
client.force_login(first_user)
first_response = client.get(
reverse("recipes:recipe-detail", kwargs={"slug": recipe.slug})
)
assertContains(first_response, '1 views')
# Second user visit
mock_redis.exists.return_value = False
mock_redis.get.return_value = b'2'
client.force_login(second_user)
second_response = client.get(
reverse("recipes:recipe-detail", kwargs={"slug": recipe.slug})
)
# FAILS: still shows "1 views"
assertContains(second_response, '2 views')
assert mock_redis.incr.call_count == 2
assert mock_redis.set.call_count == 2
Even though I change mock_redis.get.return_value to b'2' before the second request,
the response still shows "1 views".
The incr() method is called twice, but the value returned by get() does not reflect this.
What is the correct way to mock Redis in pytest-django for counters like incr/get? Is MagicMock insufficient for this case?