From 070cb1529e85b1df2ef6a1aa5859ef8cc772bd28 Mon Sep 17 00:00:00 2001 From: Brent O'Connor Date: Fri, 9 Aug 2019 13:26:20 -0500 Subject: [PATCH] Add new assert_http_###_status_name test methods --- test_plus/status_codes.py | 192 ++++++++++++++++++ test_plus/test.py | 3 +- test_project/test_app/tests/test_unittests.py | 38 ++++ test_project/test_app/urls.py | 2 + test_project/test_app/views.py | 9 + 5 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 test_plus/status_codes.py diff --git a/test_plus/status_codes.py b/test_plus/status_codes.py new file mode 100644 index 0000000..c04f744 --- /dev/null +++ b/test_plus/status_codes.py @@ -0,0 +1,192 @@ +class StatusCodeAssertionMixin(object): + """ + The following `assert_http_###_status_name` methods were intentionally added statically instead of dynamically so + that code completion in IDEs like PyCharm would work. It is preferred to use these methods over the response_XXX + methods, which could be deprecated at some point. The assert methods contain both the number and the status name + slug so that people that remember them best by their numeric code and people that remember best by their name will + be able to easily find the assertion they need. This was also directly patterned off of what the `Django Rest + Framework uses `_. + """ + + def _assert_http_status(self, status_code, response=None, msg=None, url=None): + response = self._which_response(response) + self.assertEqual(response.status_code, status_code, msg) + if url is not None: + self.assertEqual(response.url, url) + + def assert_http_100_continue(self, response=None, msg=None): + self._assert_http_status(100, response=response, msg=msg) + + def assert_http_101_switching_protocols(self, response=None, msg=None): + self._assert_http_status(101, response=response, msg=msg) + + def assert_http_200_ok(self, response=None, msg=None): + self._assert_http_status(200, response=response, msg=msg) + + def assert_http_201_created(self, response=None, msg=None): + self._assert_http_status(201, response=response, msg=msg) + + def assert_http_202_accepted(self, response=None, msg=None): + self._assert_http_status(202, response=response, msg=msg) + + def assert_http_203_non_authoritative_information(self, response=None, msg=None): + self._assert_http_status(203, response=response, msg=msg) + + def assert_http_204_no_content(self, response=None, msg=None): + self._assert_http_status(204, response=response, msg=msg) + + def assert_http_205_reset_content(self, response=None, msg=None): + self._assert_http_status(205, response=response, msg=msg) + + def assert_http_206_partial_content(self, response=None, msg=None): + self._assert_http_status(206, response=response, msg=msg) + + def assert_http_207_multi_status(self, response=None, msg=None): + self._assert_http_status(207, response=response, msg=msg) + + def assert_http_208_already_reported(self, response=None, msg=None): + self._assert_http_status(208, response=response, msg=msg) + + def assert_http_226_im_used(self, response=None, msg=None): + self._assert_http_status(226, response=response, msg=msg) + + def assert_http_300_multiple_choices(self, response=None, msg=None): + self._assert_http_status(300, response=response, msg=msg) + + def assert_http_301_moved_permanently(self, response=None, msg=None, url=None): + self._assert_http_status(301, response=response, msg=msg, url=url) + + def assert_http_302_found(self, response=None, msg=None, url=None): + self._assert_http_status(302, response=response, msg=msg, url=url) + + def assert_http_303_see_other(self, response=None, msg=None): + self._assert_http_status(303, response=response, msg=msg) + + def assert_http_304_not_modified(self, response=None, msg=None): + self._assert_http_status(304, response=response, msg=msg) + + def assert_http_305_use_proxy(self, response=None, msg=None): + self._assert_http_status(305, response=response, msg=msg) + + def assert_http_306_reserved(self, response=None, msg=None): + self._assert_http_status(306, response=response, msg=msg) + + def assert_http_307_temporary_redirect(self, response=None, msg=None): + self._assert_http_status(307, response=response, msg=msg) + + def assert_http_308_permanent_redirect(self, response=None, msg=None): + self._assert_http_status(308, response=response, msg=msg) + + def assert_http_400_bad_request(self, response=None, msg=None): + self._assert_http_status(400, response=response, msg=msg) + + def assert_http_401_unauthorized(self, response=None, msg=None): + self._assert_http_status(401, response=response, msg=msg) + + def assert_http_402_payment_required(self, response=None, msg=None): + self._assert_http_status(402, response=response, msg=msg) + + def assert_http_403_forbidden(self, response=None, msg=None): + self._assert_http_status(403, response=response, msg=msg) + + def assert_http_404_not_found(self, response=None, msg=None): + self._assert_http_status(404, response=response, msg=msg) + + def assert_http_405_method_not_allowed(self, response=None, msg=None): + self._assert_http_status(405, response=response, msg=msg) + + def assert_http_406_not_acceptable(self, response=None, msg=None): + self._assert_http_status(406, response=response, msg=msg) + + def assert_http_407_proxy_authentication_required(self, response=None, msg=None): + self._assert_http_status(407, response=response, msg=msg) + + def assert_http_408_request_timeout(self, response=None, msg=None): + self._assert_http_status(408, response=response, msg=msg) + + def assert_http_409_conflict(self, response=None, msg=None): + self._assert_http_status(409, response=response, msg=msg) + + def assert_http_410_gone(self, response=None, msg=None): + self._assert_http_status(410, response=response, msg=msg) + + def assert_http_411_length_required(self, response=None, msg=None): + self._assert_http_status(411, response=response, msg=msg) + + def assert_http_412_precondition_failed(self, response=None, msg=None): + self._assert_http_status(412, response=response, msg=msg) + + def assert_http_413_request_entity_too_large(self, response=None, msg=None): + self._assert_http_status(413, response=response, msg=msg) + + def assert_http_414_request_uri_too_long(self, response=None, msg=None): + self._assert_http_status(414, response=response, msg=msg) + + def assert_http_415_unsupported_media_type(self, response=None, msg=None): + self._assert_http_status(415, response=response, msg=msg) + + def assert_http_416_requested_range_not_satisfiable(self, response=None, msg=None): + self._assert_http_status(416, response=response, msg=msg) + + def assert_http_417_expectation_failed(self, response=None, msg=None): + self._assert_http_status(417, response=response, msg=msg) + + def assert_http_422_unprocessable_entity(self, response=None, msg=None): + self._assert_http_status(422, response=response, msg=msg) + + def assert_http_423_locked(self, response=None, msg=None): + self._assert_http_status(423, response=response, msg=msg) + + def assert_http_424_failed_dependency(self, response=None, msg=None): + self._assert_http_status(424, response=response, msg=msg) + + def assert_http_426_upgrade_required(self, response=None, msg=None): + self._assert_http_status(426, response=response, msg=msg) + + def assert_http_428_precondition_required(self, response=None, msg=None): + self._assert_http_status(428, response=response, msg=msg) + + def assert_http_429_too_many_requests(self, response=None, msg=None): + self._assert_http_status(429, response=response, msg=msg) + + def assert_http_431_request_header_fields_too_large(self, response=None, msg=None): + self._assert_http_status(431, response=response, msg=msg) + + def assert_http_451_unavailable_for_legal_reasons(self, response=None, msg=None): + self._assert_http_status(451, response=response, msg=msg) + + def assert_http_500_internal_server_error(self, response=None, msg=None): + self._assert_http_status(500, response=response, msg=msg) + + def assert_http_501_not_implemented(self, response=None, msg=None): + self._assert_http_status(501, response=response, msg=msg) + + def assert_http_502_bad_gateway(self, response=None, msg=None): + self._assert_http_status(502, response=response, msg=msg) + + def assert_http_503_service_unavailable(self, response=None, msg=None): + self._assert_http_status(503, response=response, msg=msg) + + def assert_http_504_gateway_timeout(self, response=None, msg=None): + self._assert_http_status(504, response=response, msg=msg) + + def assert_http_505_http_version_not_supported(self, response=None, msg=None): + self._assert_http_status(505, response=response, msg=msg) + + def assert_http_506_variant_also_negotiates(self, response=None, msg=None): + self._assert_http_status(506, response=response, msg=msg) + + def assert_http_507_insufficient_storage(self, response=None, msg=None): + self._assert_http_status(507, response=response, msg=msg) + + def assert_http_508_loop_detected(self, response=None, msg=None): + self._assert_http_status(508, response=response, msg=msg) + + def assert_http_509_bandwidth_limit_exceeded(self, response=None, msg=None): + self._assert_http_status(509, response=response, msg=msg) + + def assert_http_510_not_extended(self, response=None, msg=None): + self._assert_http_status(510, response=response, msg=msg) + + def assert_http_511_network_authentication_required(self, response=None, msg=None): + self._assert_http_status(511, response=response, msg=msg) diff --git a/test_plus/test.py b/test_plus/test.py index 14c3130..2bbe007 100644 --- a/test_plus/test.py +++ b/test_plus/test.py @@ -13,6 +13,7 @@ from django.test.utils import CaptureQueriesContext from django.utils.functional import curry +from test_plus.status_codes import StatusCodeAssertionMixin from .compat import reverse, NoReverseMatch, get_api_client @@ -76,7 +77,7 @@ def __exit__(self, *args): self.testcase.client.logout() -class BaseTestCase(object): +class BaseTestCase(StatusCodeAssertionMixin): """ Django TestCase with helpful additional features """ diff --git a/test_project/test_app/tests/test_unittests.py b/test_project/test_app/tests/test_unittests.py index 65a58ed..3117ff9 100644 --- a/test_project/test_app/tests/test_unittests.py +++ b/test_project/test_app/tests/test_unittests.py @@ -1,3 +1,5 @@ +import re + import django import factory import sys @@ -209,6 +211,42 @@ def test_delete_follow(self): res = self.delete(url, follow=True) self.assertTrue(res.status_code, 200) + @staticmethod + def _test_http_response(method, response=None, msg=None, url=None): + try: + if url is not None: + method(response=response, msg=msg, url=url) + else: + method(response=response, msg=msg) + except AssertionError as e: + msg = '{method_name}: {error}'.format(method_name=method.__name__, error=e) + e.args = (msg,) + raise + + def test_http_status_code_assertions(self): + """ + This test iterates through all the http_###_status_code methods in the StatusCodeAssertionMixin and tests that + they return the correct status code. + """ + from test_plus.status_codes import StatusCodeAssertionMixin + for attr in dir(StatusCodeAssertionMixin): + method = getattr(self, attr, None) + match = re.match(r'[a-z_]+(?P[\d]+)[a-z_]+', attr) + if callable(method) is True and match is not None: + status_code = int(match.groupdict()['status_code']) + url = self.reverse('status-code-view', status_code) + res_url = None + res = self.get(url) + + if status_code in (301, 302): + res_url = self.reverse('view-200') + + # with response + self._test_http_response(method, res, url=res_url) + + # without response + self._test_http_response(method, url=res_url) + def test_get_check_200(self): res = self.get_check_200('view-200') self.assertTrue(res.status_code, 200) diff --git a/test_project/test_app/urls.py b/test_project/test_app/urls.py index 24e43b5..150bde1 100644 --- a/test_project/test_app/urls.py +++ b/test_project/test_app/urls.py @@ -9,10 +9,12 @@ view_410, view_contains, view_context_with, view_context_without, view_headers, view_is_ajax, view_json, view_redirect, CBLoginRequiredView, CBView, + status_code_view, ) urlpatterns = [ url(r'^accounts/', include('django.contrib.auth.urls')), + url(r'^status-code-view/(?P[\d]+)/$', status_code_view, name='status-code-view'), url(r'^view/200/$', view_200, name='view-200'), url(r'^view/201/$', view_201, name='view-201'), url(r'^view/204/$', view_204, name='view-204'), diff --git a/test_project/test_app/views.py b/test_project/test_app/views.py index bd92862..16e22d2 100644 --- a/test_project/test_app/views.py +++ b/test_project/test_app/views.py @@ -17,6 +17,15 @@ # Function-based test views +def status_code_view(request, status=200): + status = int(status) + if status in (301, 302): + is_perm = True if status == 301 else False + return redirect('view-200', permanent=is_perm) + + return HttpResponse('', status=status) + + def view_200(request): return HttpResponse('', status=200)