Django/Python 单元测试:如何强制 Try/Except 块的异常
Django/Python unittesting: How to Force Exception of Try/Except Block
有没有办法强制执行 except 块,以便我可以通过单元测试验证错误 messages/handling?例如:
views.py
def get_books(request):
...
try:
book = Books.objects.get_or_create(user=request.user)
except:
messages.error(request, 'Unable to create new book!')
return HttpResponseRedirect(reverse('my_books'))
# more try/except cases
所以在这个例子中,我可以尝试模拟一个 ORM 失败,或者我可以尝试为这种情况发送一个无效的用户。但是有没有一种通用的方法来抛出异常(我愿意使用库)? Mock
例如。还是有其他方法可以使用 unittest
来强制异常?显然整个代码中会有很多 try/except 块,所以我正在寻找任何帮助。
您可以使用 unittest.mock
来完成。您应该通过 patch.object
修补 Books.objects
对象并使用 side_effect
引发异常。对您的方法异常行为的完整测试应该是:
import unittest
from unittest.mock import patch, ANY
from django.test.client import RequestFactory
class TestDjango(unittest.TestCase):
@patch("your_module.messages")
@patch("your_module.HttpResponseRedirect")
def test_get_books_exception(self,mock_redirect,mock_messages)
r = RequestFactory().get('/I_dont_konw_how_to_build_your_request/')
objs = Books.objects
with patch.object(objs,"get_or_create", side_effect=Exception()):
self.assertIs(get_books(r),mock_redirect.return_value)
mock_messages.error.assert_called_with(r, ANY)
mock_redirect.assert_called_with(reverse('my_books'))
我用 ANY
删除了字符串依赖项。我不确定你想在你的代码中测试什么(完整的行为或只是重定向......),无论如何我写了一个完整的例子。
如果您的项目不是遗留项目,请考虑以 TDD 风格重写您的方法(以及新的 django 的 ORM 调用),例如,您有一个 _get_books_proxy()
函数,它只调用 django ORM 方法:
def _get_books_proxy(request):
return Books.objects.get_or_create(user=request.user)
现在由 patch
装饰器直接修补 _get_books_proxy()
为:
@patch("your_module.messages")
@patch("your_module.HttpResponseRedirect")
@patch("your_module._get_books_proxy", side_effect=Exception())
def test_get_books_exception(self,mock_get_book, mock_redirect, mock_messages)
r = RequestFactory().get('/I_dont_konw_how_to_build_your_request/')
self.assertIs(get_books(r),mock_redirect.return_value)
mock_messages.error.assert_called_with(r, ANY)
mock_redirect.assert_called_with(reverse('my_books'))
此外 _get_books_proxy()
可以通过修补 Books
并传递一个简单的 Mock()
:
来测试行为
@patch("your_module.Books")
def test__get_books_proxy(self,mock_books):
r = Mock()
self.assertIs(mock_books.objects.get_or_create.return_value, your_module._get_books_proxy(r))
mock_books.objects.get_or_create.assert_called_with(user=r.user)
现在你有一个哨兵,如果你的临界点行为将被改变,它可以屈服于你。
如果您发现一些错误,我深表歉意(我无法测试)...但我希望思路清晰。
有没有办法强制执行 except 块,以便我可以通过单元测试验证错误 messages/handling?例如:
views.py
def get_books(request):
...
try:
book = Books.objects.get_or_create(user=request.user)
except:
messages.error(request, 'Unable to create new book!')
return HttpResponseRedirect(reverse('my_books'))
# more try/except cases
所以在这个例子中,我可以尝试模拟一个 ORM 失败,或者我可以尝试为这种情况发送一个无效的用户。但是有没有一种通用的方法来抛出异常(我愿意使用库)? Mock
例如。还是有其他方法可以使用 unittest
来强制异常?显然整个代码中会有很多 try/except 块,所以我正在寻找任何帮助。
您可以使用 unittest.mock
来完成。您应该通过 patch.object
修补 Books.objects
对象并使用 side_effect
引发异常。对您的方法异常行为的完整测试应该是:
import unittest
from unittest.mock import patch, ANY
from django.test.client import RequestFactory
class TestDjango(unittest.TestCase):
@patch("your_module.messages")
@patch("your_module.HttpResponseRedirect")
def test_get_books_exception(self,mock_redirect,mock_messages)
r = RequestFactory().get('/I_dont_konw_how_to_build_your_request/')
objs = Books.objects
with patch.object(objs,"get_or_create", side_effect=Exception()):
self.assertIs(get_books(r),mock_redirect.return_value)
mock_messages.error.assert_called_with(r, ANY)
mock_redirect.assert_called_with(reverse('my_books'))
我用 ANY
删除了字符串依赖项。我不确定你想在你的代码中测试什么(完整的行为或只是重定向......),无论如何我写了一个完整的例子。
如果您的项目不是遗留项目,请考虑以 TDD 风格重写您的方法(以及新的 django 的 ORM 调用),例如,您有一个 _get_books_proxy()
函数,它只调用 django ORM 方法:
def _get_books_proxy(request):
return Books.objects.get_or_create(user=request.user)
现在由 patch
装饰器直接修补 _get_books_proxy()
为:
@patch("your_module.messages")
@patch("your_module.HttpResponseRedirect")
@patch("your_module._get_books_proxy", side_effect=Exception())
def test_get_books_exception(self,mock_get_book, mock_redirect, mock_messages)
r = RequestFactory().get('/I_dont_konw_how_to_build_your_request/')
self.assertIs(get_books(r),mock_redirect.return_value)
mock_messages.error.assert_called_with(r, ANY)
mock_redirect.assert_called_with(reverse('my_books'))
此外 _get_books_proxy()
可以通过修补 Books
并传递一个简单的 Mock()
:
@patch("your_module.Books")
def test__get_books_proxy(self,mock_books):
r = Mock()
self.assertIs(mock_books.objects.get_or_create.return_value, your_module._get_books_proxy(r))
mock_books.objects.get_or_create.assert_called_with(user=r.user)
现在你有一个哨兵,如果你的临界点行为将被改变,它可以屈服于你。
如果您发现一些错误,我深表歉意(我无法测试)...但我希望思路清晰。