Django: referencing a namespace in a template during a unit test gives "not a registered namespace" error

Question:

I am just learning how to unit test so I’m sure I’m not understanding some core principal.

I have custom 400 and 500 error handlers that return a template indicating the error to the user. Now, in practice, when I run my site, these work perfectly! Errors get caught and the templates get generated exactly as anticipated. The difficulty comes when I try to write unit tests for the 500 error.

What occurs: any references to a namespace (in this case, ‘base’) throws an error.

Traceback (most recent call last):
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/urls/base.py", line 71, in reverse
    extra, resolver = resolver.namespace_dict[ns]
KeyError: 'base'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/--------/Documents/projects/--------/base/testing/test_views.py", line 50, in test_handler_renders_template_response
    response = self.client.get('/500/')
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/client.py", line 836, in get
    response = super().get(path, data=data, secure=secure, **extra)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/client.py", line 424, in get
    return self.generic(
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/client.py", line 541, in generic
    return self.request(**r)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/client.py", line 805, in request
    response = self.handler(environ)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/client.py", line 153, in __call__
    response = self.get_response(request)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/sentry_sdk/integrations/django/__init__.py", line 409, in sentry_patched_get_response
    rv = old_get_response(self, request)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 140, in get_response
    response = self._middleware_chain(request)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 57, in inner
    response = response_for_exception(request, exc)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 140, in response_for_exception
    response = handle_uncaught_exception(
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 185, in handle_uncaught_exception
    return callback(request)
  File "/home/--------/Documents/projects/--------/base/views.py", line 22, in handler500
    render(request, 'base/html/templates/500.html')
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/sentry_sdk/integrations/django/templates.py", line 95, in render
    return real_render(request, template_name, context, *args, **kwargs)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/shortcuts.py", line 24, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/loader.py", line 62, in render_to_string
    return template.render(context, request)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/backends/django.py", line 62, in render
    return self.template.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 175, in render
    return self._render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/loader_tags.py", line 208, in render
    return template.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 177, in render
    return self._render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/template/defaulttags.py", line 472, in render
    url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)
  File "/home/--------/Documents/projects/--------/venv/lib/python3.10/site-packages/django/urls/base.py", line 82, in reverse
    raise NoReverseMatch("%s is not a registered namespace" % key)
django.urls.exceptions.NoReverseMatch: 'base' is not a registered namespace

Here is the template:

{% extends 'base/html/templates/base.html' %}
{% load static %}
{% block body %}
    <div class="alert alert-warning">
        <h4 class="alert-heading">500 Error: Server error</h4>
        <p>
            Something generated an error that couldn't be fixed. If you want, email me at <email address> and let me know what happened.
       </p>
    </div>
{% endblock body %}

Here is the unit test:

from django.test import SimpleTestCase, override_settings
from django.urls import reverse
from project.settings import ROOT_URLCONF
from django.urls import path
from django.shortcuts import render
from ..views import handler500


def raise_500(request):
    return 1/0

urlpatterns = [
    path('500/', raise_500),
]

handler500 = handler500


@override_settings(ROOT_URLCONF=__name__)
class Test_500(SimpleTestCase):

    def test_handler_renders_template_response(self):
        self.client.raise_request_exception = False
        response = self.client.get('/500/')
        self.assertEqual(response.status_code, 500)
        self.assertTemplateUsed('base/html/templates/500.html')

If I remove the {% extends %} tag from the template, the unit test passes! In fact, all the template tags work except for the ones that reference a "namespace"; so neither {% extends %} nor {% url %} tags work, but {% static %}, {% block %}, etc., are rendered as expected.

I’m not sure what’s going on. To be honest it’s not a big problem: to satisfy the unit test I just use a static 500 page with one hard-coded link to ‘/’, and everything is fine. But in my effort to learn how to test properly, I’m curious as to why I’m getting these namespace errors just because I used {% extends %} or {% url %} in the template.

Asked By: Nathaniel Hoyt

||

Answers:

You could be overriding your app urls.py by defining your own urlpatterns= [...] in the test script. This would mean your usual namespace info isn’t available. In that case, urlpatterns.append(...) might work better.

Answered By: SamSparx