import django.template.loader import pytest from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.http import Http404 from django.template import TemplateDoesNotExist, engines from django.test import TestCase, override_settings from django.urls import path from rest_framework import status from rest_framework.decorators import api_view, renderer_classes from rest_framework.exceptions import ValidationError from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.response import Response @api_view(('GET',)) @renderer_classes((TemplateHTMLRenderer,)) def example(request): """ A view that can returns an HTML representation. """ data = {'object': 'foobar'} return Response(data, template_name='example.html') @api_view(('GET',)) @renderer_classes((TemplateHTMLRenderer,)) def permission_denied(request): raise PermissionDenied() @api_view(('GET',)) @renderer_classes((TemplateHTMLRenderer,)) def not_found(request): raise Http404() @api_view(('GET',)) @renderer_classes((TemplateHTMLRenderer,)) def validation_error(request): raise ValidationError('error') urlpatterns = [ path('', example), path('permission_denied', permission_denied), path('not_found', not_found), path('validation_error', validation_error), ] @override_settings(ROOT_URLCONF='tests.test_htmlrenderer') class TemplateHTMLRendererTests(TestCase): def setUp(self): class MockResponse: template_name = None self.mock_response = MockResponse() self._monkey_patch_get_template() def _monkey_patch_get_template(self): """ Monkeypatch get_template """ self.get_template = django.template.loader.get_template def get_template(template_name, dirs=None): if template_name == 'example.html': return engines['django'].from_string("example: {{ object }}") raise TemplateDoesNotExist(template_name) def select_template(template_name_list, dirs=None, using=None): if template_name_list == ['example.html']: return engines['django'].from_string("example: {{ object }}") raise TemplateDoesNotExist(template_name_list[0]) django.template.loader.get_template = get_template django.template.loader.select_template = select_template def tearDown(self): """ Revert monkeypatching """ django.template.loader.get_template = self.get_template def test_simple_html_view(self): response = self.client.get('/') self.assertContains(response, "example: foobar") self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_not_found_html_view(self): response = self.client.get('/not_found') self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.content, b"404 Not Found") self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_permission_denied_html_view(self): response = self.client.get('/permission_denied') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.content, b"403 Forbidden") self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_validation_error_html_view(self): response = self.client.get('/validation_error') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.content, b"400 Bad Request") self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') # 2 tests below are based on order of if statements in corresponding method # of TemplateHTMLRenderer def test_get_template_names_returns_own_template_name(self): renderer = TemplateHTMLRenderer() renderer.template_name = 'test_template' template_name = renderer.get_template_names(self.mock_response, view={}) assert template_name == ['test_template'] def test_get_template_names_returns_view_template_name(self): renderer = TemplateHTMLRenderer() class MockResponse: template_name = None class MockView: def get_template_names(self): return ['template from get_template_names method'] class MockView2: template_name = 'template from template_name attribute' template_name = renderer.get_template_names(self.mock_response, MockView()) assert template_name == ['template from get_template_names method'] template_name = renderer.get_template_names(self.mock_response, MockView2()) assert template_name == ['template from template_name attribute'] def test_get_template_names_raises_error_if_no_template_found(self): renderer = TemplateHTMLRenderer() with pytest.raises(ImproperlyConfigured): renderer.get_template_names(self.mock_response, view=object()) @override_settings(ROOT_URLCONF='tests.test_htmlrenderer') class TemplateHTMLRendererExceptionTests(TestCase): def setUp(self): """ Monkeypatch get_template """ self.get_template = django.template.loader.get_template def get_template(template_name): if template_name == '404.html': return engines['django'].from_string("404: {{ detail }}") if template_name == '403.html': return engines['django'].from_string("403: {{ detail }}") raise TemplateDoesNotExist(template_name) django.template.loader.get_template = get_template def tearDown(self): """ Revert monkeypatching """ django.template.loader.get_template = self.get_template def test_not_found_html_view_with_template(self): response = self.client.get('/not_found') self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertTrue(response.content in ( b"404: Not found", b"404 Not Found")) self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_permission_denied_html_view_with_template(self): response = self.client.get('/permission_denied') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertTrue(response.content in (b"403: Permission denied", b"403 Forbidden")) self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')