Skip to content

Commit

Permalink
refactor(testing): deprecate testtools support in TestCase
Browse files Browse the repository at this point in the history
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 falconry#2156
  • Loading branch information
EricGoulart committed Nov 10, 2024
1 parent d9aa5ac commit 32eaaac
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 74 deletions.
76 changes: 47 additions & 29 deletions falcon/testing/test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,72 @@
"""

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
to spin up an actual web server. Various simulation methods are
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
Expand All @@ -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:
Expand Down
45 changes: 0 additions & 45 deletions tests/test_testing.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import os
import warnings

import pytest

import falcon
Expand Down Expand Up @@ -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)

0 comments on commit 32eaaac

Please sign in to comment.