How to populate request.META in the test client?

I have a simple test case:

from django.template.loader import get_template
from django.test import TestCase, Client
from django.test.utils import override_settings

HEADERS = {
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0',
}

class RenderTestCase(TestCase):
    @override_settings(DEBUG=True)
    def test_csrf_render(self):
        c = Client(headers=HEADERS)
        req = c.request()
        t = get_template('csrf.html')
        actual = t.render({'request': req}, req)
        print(repr(actual))

Where csrf.html is just

{% csrf_token %}

The error I get is

Traceback (most recent call last):
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/test/utils.py", line 446, in inner
    return func(*args, **kwargs)
  File "/home/vinay/projects/django-base/base/test/test_components.py", line 45, in test_csrf_render
    actual = t.render({'request': req}, req)
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/template/backends/django.py", line 107, in render
    return self.template.render(context)
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/template/base.py", line 169, in render
    with context.bind_template(self):
  File "/usr/lib/python3.10/contextlib.py", line 135, in __enter__
    return next(self.gen)
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/template/context.py", line 256, in bind_template
    context = processor(self.request)
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/template/context_processors.py", line 41, in debug
    if settings.DEBUG and request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS:
AttributeError: 'HttpResponse' object has no attribute 'META'

If I comment out the override_settings, I get a slightly different error:

Traceback (most recent call last):
  File "/home/vinay/projects/django-base/base/test/test_components.py", line 45, in test_csrf_render
    actual = t.render({'request': req}, req)
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/template/backends/django.py", line 107, in render
    return self.template.render(context)
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/template/base.py", line 169, in render
    with context.bind_template(self):
  File "/usr/lib/python3.10/contextlib.py", line 135, in __enter__
    return next(self.gen)
  File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10/site-packages/django/template/context.py", line 256, in bind_template
    context = processor(self.request)
  File "/home/vinay/projects/django-base/base/views.py", line 62, in context
    ua = user_agent_parser.Parse(request.META['HTTP_USER_AGENT'])
AttributeError: 'HttpResponse' object has no attribute 'META'

Both errors are from not having a request.META, so how do I make sure request.META is populated in the test client?

You’re passing the response as the request. (client.request() makes an HTTP request…)

You want to use RequestFactory in your test as you’ve written it.

Ah, right. I was confused by:

class RequestFactory:
    def request(self, **request):
        "Construct a generic request object."
        return WSGIRequest(self._base_environ(**request))

and

class AsyncRequestFactory(RequestFactory):
    def request(self, **request):
        """Construct a generic request object."""

and

class Client(ClientMixin, RequestFactory):
    def request(self, **request):
        """
        Make a generic request.

I should have read more carefully, thanks for pointing it out :smile: