Django/Python单元测试:如何强制尝试/接受块的异常

Jam*_*mes 1 python django unit-testing

有没有办法强制执行except块,以便我可以通过单元测试验证错误消息/处理?例如:

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
Run Code Online (Sandbox Code Playgroud)

所以在这个例子中,我可以尝试模拟ORM失败,或者我可以尝试在这种情况下发送无效用户.但是有一种抛出异常的一般方法(我愿意使用库)吗?Mock例如.或者是否有其他方法可用于unittest强制例外?显然在整个代码中会有很多try/except块,所以我正在寻找任何帮助.

Mic*_*ico 5

你可以unittest.mock用来做.您应该通过修补Books.objects对象patch.object并使用它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'))
Run Code Online (Sandbox Code Playgroud)

我用过ANYdo删除字符串依赖.我不确定你想要在你的代码中测试什么(完整的行为或只是重定向......),无论如何我写了一个完整的例子.


如果您的项目不是遗留工作,请考虑以TDD样式重写您的方法(以及新的django的ORM调用),例如,您可以_get_books_proxy()调用django ORM方法:

def _get_books_proxy(request):
    return Books.objects.get_or_create(user=request.user)
Run Code Online (Sandbox Code Playgroud)

现在_get_books_proxy()patch装饰师直接补丁:

@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'))
Run Code Online (Sandbox Code Playgroud)

此外,_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)
Run Code Online (Sandbox Code Playgroud)

现在你有一个哨兵,如果你的关键点行为会改变,你可以屈服于你.

如果您发现一些错误(我无法测试),我会道歉......但我希望这个想法很明确.