diff --git a/README.md b/README.md index e39da96..cbba859 100644 --- a/README.md +++ b/README.md @@ -232,11 +232,14 @@ def test_better_status(self): self.assert_http_200_ok(response) ``` -Django-test-plus provides a majority of the status codes assertions for you. The status assertions +Django-test-plus provides assertions for all standard HTTP status codes. The status assertions can be found in their own [mixin](https://github.com/revsys/django-test-plus/blob/main/test_plus/status_codes.py) -and should be searchable if you're using an IDE like pycharm. It should be noted that in previous -versions, django-test-plus had assertion methods in the pattern of `response_###()`, which are still -available but have since been deprecated. See below for a list of those methods. +and should be searchable if you're using an IDE like pycharm. + +It should be noted that in previous versions, django-test-plus had assertion methods in the pattern +of `response_###()`. These methods are still available but are now **deprecated** and will emit +`DeprecationWarning` when used. You should migrate to the new `assert_http_###_()` +methods. See below for a list of the deprecated methods. Each of the assertion methods takes an optional Django test client `response` and a string `msg` argument that, if specified, is used as the error message when a failure occurs. The methods, diff --git a/docs/methods.rst b/docs/methods.rst index 454622d..b393ef7 100644 --- a/docs/methods.rst +++ b/docs/methods.rst @@ -148,7 +148,9 @@ With django-test-plus you can shorten that to be:: response = self.get('my-url-name') self.assert_http_200_ok(response) -Django-test-plus provides a majority of the status codes assertions for you. The status assertions can be found in their own `mixin `__ and should be searchable if you're using an IDE like pycharm. It should be noted that in previous versions, django-test-plus had assertion methods in the pattern of ``response_###()``, which are still available but have since been deprecated. See below for a list of those methods. +Django-test-plus provides assertions for all standard HTTP status codes. The status assertions can be found in their own `mixin `__ and should be searchable if you're using an IDE like pycharm. + +It should be noted that in previous versions, django-test-plus had assertion methods in the pattern of ``response_###()``. These methods are still available but are now **deprecated** and will emit ``DeprecationWarning`` when used. You should migrate to the new ``assert_http_###_()`` methods. See below for a list of the deprecated methods. Each of the assertion methods takes an optional Django test client ``response`` and a string ``msg`` argument that, if specified, is used as the error message when a failure occurs. The methods, ``assert_http_301_moved_permanently`` and ``assert_http_302_found`` also take an optional ``url`` argument that if passed, will check to make sure the ``response.url`` matches. diff --git a/test_plus/status_codes.py b/test_plus/status_codes.py index 9ce55d1..7b58aba 100644 --- a/test_plus/status_codes.py +++ b/test_plus/status_codes.py @@ -15,178 +15,253 @@ def _assert_http_status(self, status_code, response=None, msg=None, url=None): self.assertEqual(response.url, url) def assert_http_100_continue(self, response=None, msg=None): + """Server has received request headers and client should proceed to send request body.""" self._assert_http_status(100, response=response, msg=msg) def assert_http_101_switching_protocols(self, response=None, msg=None): + """Server is switching protocols as requested by the client.""" self._assert_http_status(101, response=response, msg=msg) + def assert_http_103_early_hints(self, response=None, msg=None): + """Used to return some response headers before final HTTP message.""" + self._assert_http_status(103, response=response, msg=msg) + def assert_http_200_ok(self, response=None, msg=None): + """Request succeeded.""" self._assert_http_status(200, response=response, msg=msg) def assert_http_201_created(self, response=None, msg=None): + """Request succeeded and a new resource was created.""" self._assert_http_status(201, response=response, msg=msg) def assert_http_202_accepted(self, response=None, msg=None): + """Request received but not yet acted upon.""" self._assert_http_status(202, response=response, msg=msg) def assert_http_203_non_authoritative_information(self, response=None, msg=None): + """Returned metadata is not from the origin server but from a third party.""" self._assert_http_status(203, response=response, msg=msg) def assert_http_204_no_content(self, response=None, msg=None): + """Request succeeded but no content to send back.""" self._assert_http_status(204, response=response, msg=msg) def assert_http_205_reset_content(self, response=None, msg=None): + """Request succeeded and client should reset the document view.""" self._assert_http_status(205, response=response, msg=msg) def assert_http_206_partial_content(self, response=None, msg=None): + """Request succeeded and body contains requested ranges of data.""" self._assert_http_status(206, response=response, msg=msg) def assert_http_207_multi_status(self, response=None, msg=None): + """Response conveys information about multiple resources (WebDAV).""" self._assert_http_status(207, response=response, msg=msg) def assert_http_208_already_reported(self, response=None, msg=None): + """Members of a DAV binding have already been enumerated (WebDAV).""" self._assert_http_status(208, response=response, msg=msg) def assert_http_226_im_used(self, response=None, msg=None): + """Server has fulfilled a request for the resource with instance manipulations applied.""" self._assert_http_status(226, response=response, msg=msg) def assert_http_300_multiple_choices(self, response=None, msg=None): + """Request has multiple possible responses.""" self._assert_http_status(300, response=response, msg=msg) def assert_http_301_moved_permanently(self, response=None, msg=None, url=None): + """URL of requested resource has been changed permanently.""" self._assert_http_status(301, response=response, msg=msg, url=url) def assert_http_302_found(self, response=None, msg=None, url=None): + """Resource temporarily located at a different URI.""" self._assert_http_status(302, response=response, msg=msg, url=url) def assert_http_303_see_other(self, response=None, msg=None): + """Server redirects to get the requested resource at another URI.""" self._assert_http_status(303, response=response, msg=msg) def assert_http_304_not_modified(self, response=None, msg=None): + """Response has not been modified, client can use cached version.""" self._assert_http_status(304, response=response, msg=msg) def assert_http_305_use_proxy(self, response=None, msg=None): + """Response must be accessed through a proxy (deprecated).""" self._assert_http_status(305, response=response, msg=msg) def assert_http_306_reserved(self, response=None, msg=None): + """No longer used, reserved for future use.""" self._assert_http_status(306, response=response, msg=msg) def assert_http_307_temporary_redirect(self, response=None, msg=None): + """Resource temporarily located at a different URI, method must not change.""" self._assert_http_status(307, response=response, msg=msg) def assert_http_308_permanent_redirect(self, response=None, msg=None): + """Resource permanently located at a different URI, method must not change.""" self._assert_http_status(308, response=response, msg=msg) def assert_http_400_bad_request(self, response=None, msg=None): + """Server cannot process request due to client error.""" self._assert_http_status(400, response=response, msg=msg) def assert_http_401_unauthorized(self, response=None, msg=None): + """Request requires user authentication.""" self._assert_http_status(401, response=response, msg=msg) def assert_http_402_payment_required(self, response=None, msg=None): + """Reserved for future use in digital payment systems.""" self._assert_http_status(402, response=response, msg=msg) def assert_http_403_forbidden(self, response=None, msg=None): + """Server refuses to authorize the request.""" self._assert_http_status(403, response=response, msg=msg) def assert_http_404_not_found(self, response=None, msg=None): + """Server cannot find the requested resource.""" self._assert_http_status(404, response=response, msg=msg) def assert_http_405_method_not_allowed(self, response=None, msg=None): + """Request method not supported for the requested resource.""" self._assert_http_status(405, response=response, msg=msg) def assert_http_406_not_acceptable(self, response=None, msg=None): + """Server cannot produce a response matching the Accept headers.""" self._assert_http_status(406, response=response, msg=msg) def assert_http_407_proxy_authentication_required(self, response=None, msg=None): + """Client must authenticate with the proxy.""" self._assert_http_status(407, response=response, msg=msg) def assert_http_408_request_timeout(self, response=None, msg=None): + """Server timed out waiting for the request.""" self._assert_http_status(408, response=response, msg=msg) def assert_http_409_conflict(self, response=None, msg=None): + """Request conflicts with current state of the server.""" self._assert_http_status(409, response=response, msg=msg) def assert_http_410_gone(self, response=None, msg=None): + """Requested resource is permanently unavailable.""" self._assert_http_status(410, response=response, msg=msg) def assert_http_411_length_required(self, response=None, msg=None): + """Server requires Content-Length header in the request.""" self._assert_http_status(411, response=response, msg=msg) def assert_http_412_precondition_failed(self, response=None, msg=None): + """Client's preconditions in headers are not met.""" self._assert_http_status(412, response=response, msg=msg) def assert_http_413_request_entity_too_large(self, response=None, msg=None): + """Request payload is larger than server is willing to process.""" self._assert_http_status(413, response=response, msg=msg) def assert_http_414_request_uri_too_long(self, response=None, msg=None): + """URI requested by client is longer than server can interpret.""" self._assert_http_status(414, response=response, msg=msg) def assert_http_415_unsupported_media_type(self, response=None, msg=None): + """Media format of request data is not supported.""" self._assert_http_status(415, response=response, msg=msg) def assert_http_416_requested_range_not_satisfiable(self, response=None, msg=None): + """Range specified in Range header cannot be fulfilled.""" self._assert_http_status(416, response=response, msg=msg) def assert_http_417_expectation_failed(self, response=None, msg=None): + """Expectation in Expect header cannot be met.""" self._assert_http_status(417, response=response, msg=msg) + def assert_http_418_im_a_teapot(self, response=None, msg=None): + """Server refuses to brew coffee because it is a teapot.""" + self._assert_http_status(418, response=response, msg=msg) + + def assert_http_421_misdirected_request(self, response=None, msg=None): + """Request was directed to a server unable to produce a response.""" + self._assert_http_status(421, response=response, msg=msg) + def assert_http_422_unprocessable_entity(self, response=None, msg=None): + """Request is well-formed but contains semantic errors.""" self._assert_http_status(422, response=response, msg=msg) def assert_http_423_locked(self, response=None, msg=None): + """Resource being accessed is locked (WebDAV).""" self._assert_http_status(423, response=response, msg=msg) def assert_http_424_failed_dependency(self, response=None, msg=None): + """Request failed due to failure of a previous request (WebDAV).""" self._assert_http_status(424, response=response, msg=msg) + def assert_http_425_too_early(self, response=None, msg=None): + """Server is unwilling to risk processing a request that might be replayed.""" + self._assert_http_status(425, response=response, msg=msg) + def assert_http_426_upgrade_required(self, response=None, msg=None): + """Client should switch to a different protocol.""" self._assert_http_status(426, response=response, msg=msg) def assert_http_428_precondition_required(self, response=None, msg=None): + """Server requires request to be conditional.""" self._assert_http_status(428, response=response, msg=msg) def assert_http_429_too_many_requests(self, response=None, msg=None): + """Client has sent too many requests in a given time period.""" self._assert_http_status(429, response=response, msg=msg) def assert_http_431_request_header_fields_too_large(self, response=None, msg=None): + """Request header fields are too large.""" self._assert_http_status(431, response=response, msg=msg) def assert_http_451_unavailable_for_legal_reasons(self, response=None, msg=None): + """Resource is unavailable due to legal reasons.""" self._assert_http_status(451, response=response, msg=msg) def assert_http_500_internal_server_error(self, response=None, msg=None): + """Server encountered an unexpected condition.""" self._assert_http_status(500, response=response, msg=msg) def assert_http_501_not_implemented(self, response=None, msg=None): + """Server does not support the functionality required.""" self._assert_http_status(501, response=response, msg=msg) def assert_http_502_bad_gateway(self, response=None, msg=None): + """Server received an invalid response from upstream server.""" self._assert_http_status(502, response=response, msg=msg) def assert_http_503_service_unavailable(self, response=None, msg=None): + """Server is not ready to handle the request.""" self._assert_http_status(503, response=response, msg=msg) def assert_http_504_gateway_timeout(self, response=None, msg=None): + """Server did not receive timely response from upstream server.""" self._assert_http_status(504, response=response, msg=msg) def assert_http_505_http_version_not_supported(self, response=None, msg=None): + """HTTP version used in request is not supported by server.""" self._assert_http_status(505, response=response, msg=msg) def assert_http_506_variant_also_negotiates(self, response=None, msg=None): + """Server has an internal configuration error in content negotiation.""" self._assert_http_status(506, response=response, msg=msg) def assert_http_507_insufficient_storage(self, response=None, msg=None): + """Server is unable to store the representation needed to complete the request (WebDAV).""" self._assert_http_status(507, response=response, msg=msg) def assert_http_508_loop_detected(self, response=None, msg=None): + """Server detected an infinite loop while processing the request (WebDAV).""" self._assert_http_status(508, response=response, msg=msg) def assert_http_509_bandwidth_limit_exceeded(self, response=None, msg=None): + """Bandwidth limit has been exceeded (non-standard).""" self._assert_http_status(509, response=response, msg=msg) def assert_http_510_not_extended(self, response=None, msg=None): + """Further extensions to the request are required.""" self._assert_http_status(510, response=response, msg=msg) def assert_http_511_network_authentication_required(self, response=None, msg=None): + """Client needs to authenticate to gain network access.""" self._assert_http_status(511, response=response, msg=msg) diff --git a/test_plus/test.py b/test_plus/test.py index 723dfe1..813309d 100644 --- a/test_plus/test.py +++ b/test_plus/test.py @@ -1,4 +1,5 @@ import django +import warnings try: from packaging.version import parse as parse_version @@ -181,57 +182,117 @@ def _assert_response_code(self, status_code, response=None, msg=None): self.assertEqual(response.status_code, status_code, msg) def response_200(self, response=None, msg=None): - """ Given response has status_code 200 """ + """Request succeeded.""" + warnings.warn( + 'response_200() is deprecated, use assert_http_200_ok() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(200, response, msg) def response_201(self, response=None, msg=None): - """ Given response has status_code 201 """ + """Request succeeded and a new resource was created.""" + warnings.warn( + 'response_201() is deprecated, use assert_http_201_created() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(201, response, msg) def response_204(self, response=None, msg=None): - """ Given response has status_code 204 """ + """Request succeeded but no content to send back.""" + warnings.warn( + 'response_204() is deprecated, use assert_http_204_no_content() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(204, response, msg) def response_301(self, response=None, msg=None): - """ Given response has status_code 301 """ + """URL of requested resource has been changed permanently.""" + warnings.warn( + 'response_301() is deprecated, use assert_http_301_moved_permanently() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(301, response, msg) def response_302(self, response=None, msg=None): - """ Given response has status_code 302 """ + """Resource temporarily located at a different URI.""" + warnings.warn( + 'response_302() is deprecated, use assert_http_302_found() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(302, response, msg) def response_400(self, response=None, msg=None): - """ Given response has status_code 400 """ + """Server cannot process request due to client error.""" + warnings.warn( + 'response_400() is deprecated, use assert_http_400_bad_request() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(400, response, msg) def response_401(self, response=None, msg=None): - """ Given response has status_code 401 """ + """Request requires user authentication.""" + warnings.warn( + 'response_401() is deprecated, use assert_http_401_unauthorized() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(401, response, msg) def response_403(self, response=None, msg=None): - """ Given response has status_code 403 """ + """Server refuses to authorize the request.""" + warnings.warn( + 'response_403() is deprecated, use assert_http_403_forbidden() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(403, response, msg) def response_404(self, response=None, msg=None): - """ Given response has status_code 404 """ + """Server cannot find the requested resource.""" + warnings.warn( + 'response_404() is deprecated, use assert_http_404_not_found() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(404, response, msg) def response_405(self, response=None, msg=None): - """ Given response has status_code 405 """ + """Request method not supported for the requested resource.""" + warnings.warn( + 'response_405() is deprecated, use assert_http_405_method_not_allowed() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(405, response, msg) def response_409(self, response=None, msg=None): - """ Given response has status_code 409 """ + """Request conflicts with current state of the server.""" + warnings.warn( + 'response_409() is deprecated, use assert_http_409_conflict() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(409, response, msg) def response_410(self, response=None, msg=None): - """ Given response has status_code 410 """ + """Requested resource is permanently unavailable.""" + warnings.warn( + 'response_410() is deprecated, use assert_http_410_gone() instead', + DeprecationWarning, + stacklevel=2 + ) self._assert_response_code(410, response, msg) def get_check_200(self, url, *args, **kwargs): """ Test that we can GET a page and it returns a 200 """ response = self.get(url, *args, **kwargs) - self.response_200(response) + self.assert_http_200_ok(response) return response def assertLoginRequired(self, url, *args, **kwargs): @@ -319,7 +380,7 @@ def assertGoodView(self, url_name, *args, verbose=False, **kwargs): with self.assertNumQueriesLessThan(query_count, verbose=verbose): response = self.get(url_name, *args, **kwargs) - self.response_200(response) + self.assert_http_200_ok(response) return response @@ -493,7 +554,7 @@ def get_response(self, request, view_func): def get_check_200(self, url, *args, **kwargs): """ Test that we can GET a page and it returns a 200 """ response = super(CBVTestCase, self).get(url, *args, **kwargs) - self.response_200(response) + self.assert_http_200_ok(response) return response def assertLoginRequired(self, url, *args, **kwargs): @@ -514,5 +575,5 @@ def assertGoodView(self, url_name, *args, **kwargs): with self.assertNumQueriesLessThan(query_count): response = super(CBVTestCase, self).get(url_name, *args, **kwargs) - self.response_200(response) + self.assert_http_200_ok(response) return response diff --git a/test_project/test_app/tests/test_unittests.py b/test_project/test_app/tests/test_unittests.py index 5e41343..d0d38b1 100644 --- a/test_project/test_app/tests/test_unittests.py +++ b/test_project/test_app/tests/test_unittests.py @@ -101,7 +101,8 @@ def test_print_form_errors(self): self.assertTrue('This field is required.' in output) self.post('form-errors') - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() output = StringIO() with redirect_stdout(output): self.print_form_errors() @@ -264,87 +265,111 @@ def test_get_check_200(self): def test_response_200(self): res = self.get('view-200') - self.response_200(res) + with self.assertWarns(DeprecationWarning): + self.response_200(res) # Test without response option - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() def test_response_201(self): res = self.get('view-201') - self.response_201(res) + with self.assertWarns(DeprecationWarning): + self.response_201(res) # Test without response option - self.response_201() + with self.assertWarns(DeprecationWarning): + self.response_201() def test_response_204(self): res = self.get('view-204') - self.response_204(res) + with self.assertWarns(DeprecationWarning): + self.response_204(res) # Test without response option - self.response_204() + with self.assertWarns(DeprecationWarning): + self.response_204() def test_response_301(self): res = self.get('view-301') - self.response_301(res) + with self.assertWarns(DeprecationWarning): + self.response_301(res) # Test without response option - self.response_301() + with self.assertWarns(DeprecationWarning): + self.response_301() def test_response_302(self): res = self.get('view-302') - self.response_302(res) + with self.assertWarns(DeprecationWarning): + self.response_302(res) # Test without response option - self.response_302() + with self.assertWarns(DeprecationWarning): + self.response_302() def test_response_400(self): res = self.get('view-400') - self.response_400(res) + with self.assertWarns(DeprecationWarning): + self.response_400(res) # Test without response option - self.response_400() + with self.assertWarns(DeprecationWarning): + self.response_400() def test_response_401(self): res = self.get('view-401') - self.response_401(res) + with self.assertWarns(DeprecationWarning): + self.response_401(res) # Test without response option - self.response_401() + with self.assertWarns(DeprecationWarning): + self.response_401() def test_response_403(self): res = self.get('view-403') - self.response_403(res) + with self.assertWarns(DeprecationWarning): + self.response_403(res) # Test without response option - self.response_403() + with self.assertWarns(DeprecationWarning): + self.response_403() def test_response_404(self): res = self.get('view-404') - self.response_404(res) + with self.assertWarns(DeprecationWarning): + self.response_404(res) # Test without response option - self.response_404() + with self.assertWarns(DeprecationWarning): + self.response_404() def test_response_405(self): res = self.get('view-405') - self.response_405(res) + with self.assertWarns(DeprecationWarning): + self.response_405(res) # Test without response option - self.response_405() + with self.assertWarns(DeprecationWarning): + self.response_405() def test_response_409(self): res = self.get('view-409') - self.response_409(res) + with self.assertWarns(DeprecationWarning): + self.response_409(res) # Test without response option - self.response_409() + with self.assertWarns(DeprecationWarning): + self.response_409() def test_response_410(self): res = self.get('view-410') - self.response_410(res) + with self.assertWarns(DeprecationWarning): + self.response_410(res) # Test without response option - self.response_410() + with self.assertWarns(DeprecationWarning): + self.response_410() def test_make_user(self): """ Test make_user using django.contrib.auth defaults """ @@ -459,13 +484,15 @@ def test_get_context_raises(self): def test_get_is_ajax(self): response = self.get('view-is-ajax', extra={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}) - self.response_200(response) + with self.assertWarns(DeprecationWarning): + self.response_200(response) def test_post_is_ajax(self): response = self.post('view-is-ajax', data={'item': 1}, extra={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}) - self.response_200(response) + with self.assertWarns(DeprecationWarning): + self.response_200(response) def test_assertresponsecontains(self): self.get('view-contains') @@ -484,16 +511,19 @@ class TestPlusCBViewTests(CBVTestCase): def test_get(self): self.get(CBView) - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() def test_post(self): data = {'testing': True} self.post(CBView, data=data) - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() # Test without data self.post(CBView) - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() def test_get_check_200(self): self.get_check_200('cbview') @@ -660,7 +690,8 @@ def test_post_with_content_type(self): data = {'testing': {'prop': 'value'}} response = self.post('view-json', data=json.dumps(data), extra={'content_type': 'application/json'}) assert response["content-type"] == "application/json" - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() def test_post_with_non_primitive_data_type(self): data = {"uuid": str(uuid.uuid4())} @@ -676,13 +707,15 @@ def test_get_with_content_type(self): data = {'testing': {'prop': 'value'}} response = self.get('view-json', data=data, extra={'content_type': 'application/json'}) assert response["content-type"] == "application/json" - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() def test_get_with_non_primitive_data_type(self): data = {"uuid": uuid.uuid4()} response = self.get('view-json', data=data, extra={'format': 'json'}) assert response["content-type"] == "application/json" - self.response_200() + with self.assertWarns(DeprecationWarning): + self.response_200() # pytest tests @@ -708,5 +741,7 @@ def test_tp_response_200(tp): url = tp.reverse('view-200') data = {'testing': True} res = tp.post(url, data=data) - tp.response_200() - tp.response_200(res) + with pytest.warns(DeprecationWarning): + tp.response_200() + with pytest.warns(DeprecationWarning): + tp.response_200(res) diff --git a/test_project/test_app/urls.py b/test_project/test_app/urls.py index 064b1a7..c263c44 100644 --- a/test_project/test_app/urls.py +++ b/test_project/test_app/urls.py @@ -7,9 +7,11 @@ from django.conf.urls.defaults import url, include from .views import ( - FormErrors, data_1, data_5, needs_login, view_200, view_201, view_204, - view_301, view_302, view_400, view_401, view_403, view_404, view_405, - view_409, view_410, view_contains, view_context_with, view_context_without, + FormErrors, data_1, data_5, needs_login, + view_200, view_201, view_204, + view_301, view_302, + view_400, view_401, view_403, view_404, view_405, view_409, view_410, + view_contains, view_context_with, view_context_without, view_headers, view_is_ajax, view_json, view_redirect, CBLoginRequiredView, CBView, status_code_view,