I have coded my custom Django middleware in the 1.10 style, similar to this:
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
Note, that this middleware is being threaten as a separate Python module, not belonging to any particular application in my project, but living outside and being installed like any other package, via pip. It does not work itself, but only if installed in Django app.
It works fine, however, I would like to test it. What I've made so far is something like this in 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
I don't know how to call middleware on request
variable to modify it. I think it would be much easier if I used function-like middleware style, but what if I'm stuck with what I have and I am supposed ony to write tests, without modifying middleware?
Custom middleware in Django is created either as a function style that takes a get_response callable or a class-based style whose call method is used to process requests and responses. It is created inside a file middleware.py . A middleware is activated by adding it to the MIDDLEWARE list in Django settings.
The preferred way to write tests in Django is using the unittest module built-in to the Python standard library. This is covered in detail in the Writing and running tests document. You can also use any other Python test framework; Django provides an API and tools for that kind of integration.
Writing testsDjango's unit tests use a Python standard library module: unittest . This module defines tests using a class-based approach.
Open /catalog/tests/test_models.py.TestCase , as shown: from django. test import TestCase # Create your tests here. Often you will add a test class for each model/view/form you want to test, with individual methods for testing specific functionality.
The problem is that you are not calling neither the constructor of MyMiddleware
neither invoking the __call__
magic method by invoking the instance of a MyMiddleware
object.
There are many ways to test the behaviour that you described, I can think of this one:
First, I slightly modified your example to be self contained:
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'
Next, I created the tests by actually creating the Middleware object and invoking the newly created object as it was a function (so __call__
is run)
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'
Here there are some useful links:
Difference between __call__ and __init__ in another SO question: __init__ or __call__?
Where to patch from the python docs: https://docs.python.org/3/library/unittest.mock.html#where-to-patch
pytest docs: http://docs.pytest.org/en/latest/contents.html
ipdb intro, useful for debugging: https://www.safaribooksonline.com/blog/2014/11/18/intro-python-debugger/
Okay, this is a bit late but I had a similar problem and want to provide answer for Googlers.
First, some answers I've come up with suggests to create a HttpRequest
instance with setting up a RequestFactory
in tests, but this section clearly puts that generating a HttpRequest
instance with a RequestFactory
does not work like a HTTP server and continues:
It does not support middleware. Session and authentication attributes must be supplied by the test itself if required for the view to function properly.
I already want to test if my injection is in the HttpRequest
object, so we have to create one. First, we create a dummy view:
def dummy_view(request, *args, **kwargs):
return HttpResponse(b"dummy")
Then, we need to hook this up to a URL in urls.py
.
path("/dummy", dummy_view, name="dummy")
If you already use pytest-django, then you can easily get a HttpClient
instance with client
fixture, call this URL and get HttpRequest
from response as below:
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")
This has some drawbacks though:
if DEBUG
. I find this useful only for 3rd party modules.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With