From 32eaaac5bd4a98be160ea9f154aef0bca8c6f2d9 Mon Sep 17 00:00:00 2001 From: "eric.goulart" Date: Sun, 10 Nov 2024 10:39:29 -0300 Subject: [PATCH] refactor(testing): deprecate testtools support in TestCase Remove conditional dependency on testtools in TestCase to simplify maintenance and prepare for Falcon 5.0 where testtools will no longer be supported. Instead, unittest is now the default, and users are encouraged to use pytest for testing Falcon applications. This change emits a deprecation warning if testtools is still enabled via environment variables. BREAKING CHANGE: testtools support is deprecated and scheduled for removal in Falcon 5.0. Closes #2156 --- falcon/testing/test_case.py | 76 +++++++++++++++++++++++-------------- tests/test_testing.py | 45 ---------------------- 2 files changed, 47 insertions(+), 74 deletions(-) diff --git a/falcon/testing/test_case.py b/falcon/testing/test_case.py index 1026adc86..a1b9696ce 100644 --- a/falcon/testing/test_case.py +++ b/falcon/testing/test_case.py @@ -19,31 +19,40 @@ """ import os +import unittest import warnings +if 'DISABLE_TESTTOOLS' not in os.environ: + try: + import testtools + + warnings.warn( + 'Support for testtools is deprecated and will be removed in Falcon 5.0. ' + 'Please migrate to unittest or pytest.', + DeprecationWarning, + ) + BaseTestCase = testtools.TestCase + except ImportError: # pragma: nocover + BaseTestCase = unittest.TestCase + import falcon +import falcon.request from falcon.testing.client import Result # NOQA from falcon.testing.client import TestClient -USE_TESTTOOLS = os.getenv('USE_TESTTOOLS', 'false').lower() == 'true' - -try: - if USE_TESTTOOLS: - import testtools as unittest - else: - import unittest -except ImportError: # pragma: nocover - import unittest - - USE_TESTTOOLS = False - class TestCase(unittest.TestCase, TestClient): """Extends :mod:`unittest` to support WSGI/ASGI functional testing. Note: - If available, uses :mod:`testtools` in lieu of - :mod:`unittest`. + This class uses :mod:`unittest` by default. If :mod:`testtools` + is available and the environment variable + ``DISABLE_TESTTOOLS`` is **not** set, it will use :mod:`testtools` instead. + **Support for testtools is deprecated and will be removed in Falcon 5.0.** + + Recommended: + We recommend using **pytest** for testing Falcon applications. + See our tutorial on using pytest. This base class provides some extra plumbing for unittest-style test cases, to help simulate WSGI or ASGI requests without having @@ -51,14 +60,31 @@ class TestCase(unittest.TestCase, TestClient): derived from :class:`falcon.testing.TestClient`. Simply inherit from this class in your test case classes instead of - :class:`unittest.TestCase` or :class:`testtools.TestCase`. - """ + :class:`unittest.TestCase`. - if USE_TESTTOOLS: - warnings.warn( - 'The use of "testtools.TestCase" is deprecated and will be removed ' - 'in Falcon 5.0. Please migrate to "pytest" or "unittest" ' - ) + For example:: + + from falcon import testing + import myapp + + + class MyTestCase(testing.TestCase): + def setUp(self): + super(MyTestCase, self).setUp() + + # Assume the hypothetical `myapp` package has a + # function called `create()` to initialize and + # return a `falcon.App` instance. + self.app = myapp.create() + + + class TestMyApp(MyTestCase): + def test_get_message(self): + doc = {'message': 'Hello world!'} + + result = self.simulate_get('/messages/42') + self.assertEqual(result.json, doc) + """ # NOTE(vytas): Here we have to restore __test__ to allow collecting tests! __test__ = True @@ -82,14 +108,6 @@ def setUp(self): # function called `create()` to initialize and # return a `falcon.App` instance. self.app = myapp.create() - - - class TestMyApp(MyTestCase): - def test_get_message(self): - doc = {'message': 'Hello world!'} - - result = self.simulate_get('/messages/42') - self.assertEqual(result.json, doc) """ def setUp(self) -> None: diff --git a/tests/test_testing.py b/tests/test_testing.py index fe1aa6319..89a8c49e8 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,6 +1,3 @@ -import os -import warnings - import pytest import falcon @@ -227,45 +224,3 @@ def capture_method(req, resp): assert result.status_code == 200 expected = '' if method == 'HEAD' else method assert result.text == expected - - -def test_testclient_initialization(): - class SampleResource: - def on_get(self, req, resp): - resp.media = {'message': 'Hello'} - - class SampleTestCase(testing.TestCase): - def setUp(self): - super().setUp() - self.app = falcon.App() - self.app.add_route('/hello', SampleResource()) - - test_case = SampleTestCase() - test_case.setUp() - client = testing.TestClient(test_case.app) - - response = client.simulate_get('/hello') - assert response.status_code == 200 - assert response.json == {'message': 'Hello'} - - -def test_deprecation_warning_with_testtools(): - os.environ['USE_TESTTOOLS'] = 'true' - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - - class SampleTestCase(testing.TestCase): - def setUp(self): - super().setUp() - self.app = falcon.App() - - test_case = SampleTestCase() - test_case.setUp() - - assert any( - 'The use of "testtools.TestCase" is deprecated' in str(warning.message) - for warning in w - ), 'Expected deprecation warning not found' - - os.environ.pop('USE_TESTTOOLS', None)