在不使用 Django 本身的情况下测试自定义 Django 中间件
Testing custom Django middleware without using Django itself
我在 1.10 style 中编写了自定义 Django 中间件,类似于:
class MyMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# some initialization stuff here
def __call__(self, request):
# Code executed before view functions are called.
# Purpose of this middeware is to add new attribute to request
# In brief:
request.new_attribute = some_function_returning_some_object()
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
请注意,此中间件作为一个单独的 Python 模块受到威胁,不属于我项目中的任何特定应用程序,但存在于外部并像任何其他包一样通过 pip 安装。它本身不工作,但只有安装在 Django 应用程序中。
它工作正常,但是,我想测试一下。到目前为止,我所做的是 my_tests.py
:
中的类似内容
from my_middleware_module import MyMiddleware
# some @patches
def test_mymiddleware():
request = Mock()
assert hasattr(request, 'new_attribute') is False # passes obviously
# CALL MIDDLEWARE ON REQUEST HERE
assert hasattr(request, 'new_attribute') is True # I want it to pass
我不知道如何在 request
变量上调用中间件来修改它。我认为如果我使用类似函数的中间件样式会容易得多,但是如果我坚持使用现有的东西并且我应该只编写测试而不修改中间件怎么办?
问题是您既没有调用 MyMiddleware
的构造函数,也没有通过调用 MyMiddleware
对象的实例来调用 __call__
魔法方法。
有很多方法可以测试您描述的行为,我可以想到这个:
首先,我稍微修改了您的示例使其自包含:
class MyMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.new_attribute = some_function_returning_some_object()
response = self.get_response(request)
return response
def some_function_returning_some_object():
return 'whatever'
接下来,我通过实际创建中间件对象并调用新创建的对象来创建测试,因为它是一个函数(所以 __call__
是 运行)
from mock import patch, Mock
from middle import MyMiddleware
import unittest
class TestMiddleware(unittest.TestCase):
@patch('middle.MyMiddleware')
def test_init(self, my_middleware_mock):
my_middleware = MyMiddleware('response')
assert(my_middleware.get_response) == 'response'
def test_mymiddleware(self):
request = Mock()
my_middleware = MyMiddleware(Mock())
# CALL MIDDLEWARE ON REQUEST HERE
my_middleware(request)
assert request.new_attribute == 'whatever'
这里有一些有用的链接:
另一个SO问题中__call__和__init__的区别:__init__ or __call__?
python 文档中的补丁:https://docs.python.org/3/library/unittest.mock.html#where-to-patch
ipdb 简介,对调试有用:https://www.safaribooksonline.com/blog/2014/11/18/intro-python-debugger/
好的,有点晚了,但我遇到了类似的问题,想为 Google 员工提供答案。
首先,我提出的一些答案建议创建一个 HttpRequest
实例并在测试中设置 RequestFactory
,但 this section 明确指出生成 HttpRequest
带有 RequestFactory
的实例不像 HTTP 服务器那样工作并继续:
It does not support middleware. Session and authentication attributes must be supplied by the test itself if required for the view to function properly.
我已经想测试我的注入 是否在 HttpRequest
对象中 ,所以我们必须创建一个。首先,我们创建一个虚拟视图:
def dummy_view(request, *args, **kwargs):
return HttpResponse(b"dummy")
然后,我们需要将其连接到 urls.py
中的 URL。
path("/dummy", dummy_view, name="dummy")
如果您已经使用了 pytest-django,那么您可以轻松地使用 client
fixture 获取 HttpClient
实例,调用此 URL 并从中获取 HttpRequest
回复如下:
def test_request_injection_exists(client):
response = client.get(reverse("dummy"))
request = response.wsgi_request # this has `HttpRequest` instance
# assuming I injected an attribute called `foo`
assert hasattr(request, "foo")
虽然这有一些缺点:
- 它产生了一个真正的开发服务器进程,所以它使测试有点慢。
- 创建一个虚拟视图对于真正的 Django 项目可能不是一个可行的解决方案,因为您可能需要用太多的样板文件包装您的 URL,例如
if DEBUG
。我发现这仅对第 3 方模块有用。
我在 1.10 style 中编写了自定义 Django 中间件,类似于:
class MyMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# some initialization stuff here
def __call__(self, request):
# Code executed before view functions are called.
# Purpose of this middeware is to add new attribute to request
# In brief:
request.new_attribute = some_function_returning_some_object()
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
请注意,此中间件作为一个单独的 Python 模块受到威胁,不属于我项目中的任何特定应用程序,但存在于外部并像任何其他包一样通过 pip 安装。它本身不工作,但只有安装在 Django 应用程序中。
它工作正常,但是,我想测试一下。到目前为止,我所做的是 my_tests.py
:
from my_middleware_module import MyMiddleware
# some @patches
def test_mymiddleware():
request = Mock()
assert hasattr(request, 'new_attribute') is False # passes obviously
# CALL MIDDLEWARE ON REQUEST HERE
assert hasattr(request, 'new_attribute') is True # I want it to pass
我不知道如何在 request
变量上调用中间件来修改它。我认为如果我使用类似函数的中间件样式会容易得多,但是如果我坚持使用现有的东西并且我应该只编写测试而不修改中间件怎么办?
问题是您既没有调用 MyMiddleware
的构造函数,也没有通过调用 MyMiddleware
对象的实例来调用 __call__
魔法方法。
有很多方法可以测试您描述的行为,我可以想到这个:
首先,我稍微修改了您的示例使其自包含:
class MyMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.new_attribute = some_function_returning_some_object()
response = self.get_response(request)
return response
def some_function_returning_some_object():
return 'whatever'
接下来,我通过实际创建中间件对象并调用新创建的对象来创建测试,因为它是一个函数(所以 __call__
是 运行)
from mock import patch, Mock
from middle import MyMiddleware
import unittest
class TestMiddleware(unittest.TestCase):
@patch('middle.MyMiddleware')
def test_init(self, my_middleware_mock):
my_middleware = MyMiddleware('response')
assert(my_middleware.get_response) == 'response'
def test_mymiddleware(self):
request = Mock()
my_middleware = MyMiddleware(Mock())
# CALL MIDDLEWARE ON REQUEST HERE
my_middleware(request)
assert request.new_attribute == 'whatever'
这里有一些有用的链接:
另一个SO问题中__call__和__init__的区别:__init__ or __call__?
python 文档中的补丁:https://docs.python.org/3/library/unittest.mock.html#where-to-patch
ipdb 简介,对调试有用:https://www.safaribooksonline.com/blog/2014/11/18/intro-python-debugger/
好的,有点晚了,但我遇到了类似的问题,想为 Google 员工提供答案。
首先,我提出的一些答案建议创建一个 HttpRequest
实例并在测试中设置 RequestFactory
,但 this section 明确指出生成 HttpRequest
带有 RequestFactory
的实例不像 HTTP 服务器那样工作并继续:
It does not support middleware. Session and authentication attributes must be supplied by the test itself if required for the view to function properly.
我已经想测试我的注入 是否在 HttpRequest
对象中 ,所以我们必须创建一个。首先,我们创建一个虚拟视图:
def dummy_view(request, *args, **kwargs):
return HttpResponse(b"dummy")
然后,我们需要将其连接到 urls.py
中的 URL。
path("/dummy", dummy_view, name="dummy")
如果您已经使用了 pytest-django,那么您可以轻松地使用 client
fixture 获取 HttpClient
实例,调用此 URL 并从中获取 HttpRequest
回复如下:
def test_request_injection_exists(client):
response = client.get(reverse("dummy"))
request = response.wsgi_request # this has `HttpRequest` instance
# assuming I injected an attribute called `foo`
assert hasattr(request, "foo")
虽然这有一些缺点:
- 它产生了一个真正的开发服务器进程,所以它使测试有点慢。
- 创建一个虚拟视图对于真正的 Django 项目可能不是一个可行的解决方案,因为您可能需要用太多的样板文件包装您的 URL,例如
if DEBUG
。我发现这仅对第 3 方模块有用。