Mock模拟Django模型DoesNotExist异常时捕获失败问题咨询
The Problem
I'm writing unit tests for a Django function, using Mock to simulate a missing object scenario:
from models import MyObject mock_obj.objects.get.side_effect = mock.Mock(side_effect=MyObject.DoesNotExist)
When my function catches a generic Exception, the test passes:
import models try: obj = models.MyObject.objects.get(id=1) except Exception: return True
But when I switch to catching the specific models.MyObject.DoesNotExist exception, the test fails with this traceback:
Traceback (most recent call last):
File "/home/test/test_my_function.py", line 40, in test_get_job_not_exist
response = my_function.my_function(request_mock, 1)
File "/home/handlers/my_function.py", line 35, in get_job_with_id
obj = MyObject.objects.get(id=id)
File "/local/lib/python2.7/site-packages/mock/mock.py", line 1062, in call
return _mock_self._mock_call(*args, **kwargs)
File "/local/lib/python2.7/site-packages/mock/mock.py", line 1118, in _mock_call
raise effect
DoesNotExist
MyObject is a Django model. Why isn't the specific exception catch working?
What's Going Wrong & How to Fix It
There are two key issues here that are breaking your specific exception handling:
1. You're wrapping the exception in an unnecessary Mock
Your current code sets the side_effect to a Mock instance that throws the exception, instead of directly using the exception class itself.
When you write:
mock_obj.objects.get.side_effect = mock.Mock(side_effect=MyObject.DoesNotExist)
You're creating a nested Mock: when get() runs, it triggers the inner Mock, which then throws the exception. This can break the identity check Django uses for model-specific exceptions (since each model gets its own unique DoesNotExist subclass at runtime).
Fix: Assign the exception class directly to side_effect:
# Correct approach: no extra Mock wrapper mock_obj.objects.get.side_effect = MyObject.DoesNotExist
Or if you want to include a custom error message:
mock_obj.objects.get.side_effect = MyObject.DoesNotExist("Object with ID 1 not found")
2. You might be mocking the wrong manager instance
In Python, imports are module-specific. If your test file imports MyObject directly from models, but your function uses import models and references models.MyObject.objects, you could be mocking a different manager instance than the one your function is actually using.
Django ties model managers to the model class in the module where they're referenced. To fix this, mock the exact path your function uses to access objects.get.
For example, if your function lives in my_function.py and uses models.MyObject.objects.get, mock my_function.models.MyObject.objects.get instead of your test file's models.MyObject.objects.get:
from unittest.mock import patch from my_function import my_function def test_get_job_not_exist(): # Mock the exact path the function uses to access get() with patch('my_function.models.MyObject.objects.get') as mock_get: mock_get.side_effect = MyObject.DoesNotExist # Run your function result = my_function(request_mock, 1) assert result is True
Why This Works
Django generates a unique DoesNotExist subclass for every model at runtime. When you mock correctly, you ensure the exception being thrown is exactly the same class instance that your function is trying to catch. Wrapping it in an extra Mock or targeting the wrong import path breaks this identity, causing the exception to slip past your specific catch block.
内容的提问来源于stack exchange,提问作者Pear

