when it is available (and continue to use the standard ``unittest`` module
when ``unittest2`` is not available)
* `tornado.testing.ExpectLog` can be used as a finer-grained alternative
- to `tornado.testing.LogTrapTestCase`
+ to ``tornado.testing.LogTrapTestCase``
* The command-line interface to `tornado.testing.main` now supports
additional arguments from the underlying `unittest` module:
``verbose``, ``quiet``, ``failfast``, ``catch``, ``buffer``.
instead of putting all possible options in `tornado.testing.main`.
* `.AsyncHTTPTestCase` no longer calls `.AsyncHTTPClient.close` for tests
that use the singleton `.IOLoop.instance`.
-* `.LogTrapTestCase` no longer fails when run in unknown logging
+* ``LogTrapTestCase`` no longer fails when run in unknown logging
configurations. This allows tests to be run under nose, which does its
- own log buffering (`.LogTrapTestCase` doesn't do anything useful in this
+ own log buffering (``LogTrapTestCase`` doesn't do anything useful in this
case, but at least it doesn't break things any more).
``tornado.util``
of the `ssl.match_hostname` function.
* `tornado.websocket.websocket_connect` now fails cleanly when it attempts
to connect to a non-websocket url.
-* `tornado.testing.LogTrapTestCase` once again works with byte strings
+* ``tornado.testing.LogTrapTestCase`` once again works with byte strings
on Python 2.
* The ``request`` attribute of `tornado.httpclient.HTTPResponse` is
now always an `~tornado.httpclient.HTTPRequest`, never a ``_RequestProxy``.
.. autoclass:: ExpectLog
:members:
- .. autoclass:: LogTrapTestCase
- :members:
-
Test runner
-----------
import threading
from tornado import gen
from tornado.options import parse_command_line
-from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase
+from tornado.testing import AsyncHTTPTestCase
from tornado.web import RequestHandler, Application, asynchronous
import unittest
headers=[('If-None-Match', etags)],
expected_status=200)
-class DefaultHTTPTest(AsyncHTTPTestCase, LogTrapTestCase, TestMixin):
+class DefaultHTTPTest(AsyncHTTPTestCase, TestMixin):
def get_app(self):
return Application(self.get_handlers(), **self.get_app_kwargs())
-class GzipHTTPTest(AsyncHTTPTestCase, LogTrapTestCase, TestMixin):
+class GzipHTTPTest(AsyncHTTPTestCase, TestMixin):
def get_app(self):
return Application(self.get_handlers(), gzip=True, **self.get_app_kwargs())
from tornado.log import app_log
from tornado import stack_context
from tornado.tcpserver import TCPServer
-from tornado.testing import AsyncTestCase, ExpectLog, LogTrapTestCase, bind_unused_port, gen_test
+from tornado.testing import AsyncTestCase, ExpectLog, bind_unused_port, gen_test
from tornado.test.util import unittest
class CapServer(TCPServer):
def handle_stream(self, stream, address):
- logging.info("handle_stream")
+ logging.debug("handle_stream")
self.stream = stream
self.stream.read_until(b"\n", self.handle_read)
def handle_read(self, data):
- logging.info("handle_read")
+ logging.debug("handle_read")
data = to_unicode(data)
if data == data.upper():
self.stream.write(b"error\talready capitalized\n")
class ManualCapClient(BaseCapClient):
def capitalize(self, request_data, callback=None):
- logging.info("capitalize")
+ logging.debug("capitalize")
self.request_data = request_data
self.stream = IOStream(socket.socket())
self.stream.connect(('127.0.0.1', self.port),
return self.future
def handle_connect(self):
- logging.info("handle_connect")
+ logging.debug("handle_connect")
self.stream.write(utf8(self.request_data + "\n"))
self.stream.read_until(b'\n', callback=self.handle_read)
def handle_read(self, data):
- logging.info("handle_read")
+ logging.debug("handle_read")
self.stream.close()
try:
self.future.set_result(self.process_response(data))
class DecoratorCapClient(BaseCapClient):
@return_future
def capitalize(self, request_data, callback):
- logging.info("capitalize")
+ logging.debug("capitalize")
self.request_data = request_data
self.stream = IOStream(socket.socket())
self.stream.connect(('127.0.0.1', self.port),
self.callback = callback
def handle_connect(self):
- logging.info("handle_connect")
+ logging.debug("handle_connect")
self.stream.write(utf8(self.request_data + "\n"))
self.stream.read_until(b'\n', callback=self.handle_read)
def handle_read(self, data):
- logging.info("handle_read")
+ logging.debug("handle_read")
self.stream.close()
self.callback(self.process_response(data))
@return_future
@gen.engine
def capitalize(self, request_data, callback):
- logging.info('capitalize')
+ logging.debug('capitalize')
stream = IOStream(socket.socket())
- logging.info('connecting')
+ logging.debug('connecting')
yield gen.Task(stream.connect, ('127.0.0.1', self.port))
stream.write(utf8(request_data + '\n'))
- logging.info('reading')
+ logging.debug('reading')
data = yield gen.Task(stream.read_until, b'\n')
- logging.info('returning')
+ logging.debug('returning')
stream.close()
callback(self.process_response(data))
self.wait()
-class ManualClientTest(ClientTestMixin, AsyncTestCase, LogTrapTestCase):
+class ManualClientTest(ClientTestMixin, AsyncTestCase):
client_class = ManualCapClient
-class DecoratorClientTest(ClientTestMixin, AsyncTestCase, LogTrapTestCase):
+class DecoratorClientTest(ClientTestMixin, AsyncTestCase):
client_class = DecoratorCapClient
-class GeneratorClientTest(ClientTestMixin, AsyncTestCase, LogTrapTestCase):
+class GeneratorClientTest(ClientTestMixin, AsyncTestCase):
client_class = GeneratorCapClient
* `AsyncTestCase` and `AsyncHTTPTestCase`: Subclasses of unittest.TestCase
with additional support for testing asynchronous (`.IOLoop`-based) code.
-* `ExpectLog` and `LogTrapTestCase`: Make test logs less spammy.
+* `ExpectLog`: Make test logs less spammy.
* `main()`: A simple test runner (wrapper around unittest.main()) with support
for the tornado.autoreload module to rerun the tests when code changes.
from tornado.process import Subprocess
except ImportError:
# These modules are not importable on app engine. Parts of this module
- # won't work, but e.g. LogTrapTestCase and main() will.
+ # won't work, but e.g. main() will.
AsyncHTTPClient = None # type: ignore
gen = None # type: ignore
HTTPServer = None # type: ignore
gen_test.__test__ = False # type: ignore
-class LogTrapTestCase(unittest.TestCase):
- """A test case that captures and discards all logging output
- if the test passes.
-
- Some libraries can produce a lot of logging output even when
- the test succeeds, so this class can be useful to minimize the noise.
- Simply use it as a base class for your test case. It is safe to combine
- with AsyncTestCase via multiple inheritance
- (``class MyTestCase(AsyncHTTPTestCase, LogTrapTestCase):``)
-
- This class assumes that only one log handler is configured and
- that it is a `~logging.StreamHandler`. This is true for both
- `logging.basicConfig` and the "pretty logging" configured by
- `tornado.options`. It is not compatible with other log buffering
- mechanisms, such as those provided by some test runners.
-
- .. deprecated:: 4.1
- Use the unittest module's ``--buffer`` option instead, or `.ExpectLog`.
- """
- def run(self, result=None):
- logger = logging.getLogger()
- if not logger.handlers:
- logging.basicConfig()
- handler = logger.handlers[0]
- if (len(logger.handlers) > 1 or
- not isinstance(handler, logging.StreamHandler)):
- # Logging has been configured in a way we don't recognize,
- # so just leave it alone.
- super(LogTrapTestCase, self).run(result)
- return
- old_stream = handler.stream
- try:
- handler.stream = StringIO()
- gen_log.info("RUNNING TEST: " + str(self))
- old_error_count = len(result.failures) + len(result.errors)
- super(LogTrapTestCase, self).run(result)
- new_error_count = len(result.failures) + len(result.errors)
- if new_error_count != old_error_count:
- old_stream.write(handler.stream.getvalue())
- finally:
- handler.stream = old_stream
-
-
class ExpectLog(logging.Filter):
"""Context manager to capture and suppress expected log output.