mirror of https://github.com/python/cpython.git
#10535: Enable silenced warnings in unittest by default
This commit is contained in:
parent
00f2f97dbd
commit
6090187656
|
@ -1845,12 +1845,21 @@ Loading and running tests
|
|||
instead of repeatedly creating new instances.
|
||||
|
||||
|
||||
.. class:: TextTestRunner(stream=sys.stderr, descriptions=True, verbosity=1, runnerclass=None)
|
||||
.. class:: TextTestRunner(stream=sys.stderr, descriptions=True, verbosity=1, runnerclass=None, warnings=None)
|
||||
|
||||
A basic test runner implementation which prints results on standard error. It
|
||||
has a few configurable parameters, but is essentially very simple. Graphical
|
||||
applications which run test suites should provide alternate implementations.
|
||||
|
||||
By default this runner shows :exc:`DeprecationWarning`,
|
||||
:exc:`PendingDeprecationWarning`, and :exc:`ImportWarning` even if they are
|
||||
:ref:`ignored by default <warning-ignored>`. Deprecation warnings caused by
|
||||
:ref:`deprecated unittest methods <deprecated-aliases>` are also
|
||||
special-cased and, when the warning filters are ``'default'`` or ``'always'``,
|
||||
they will appear only once per-module, in order to avoid too many warning
|
||||
messages. This behavior can be overridden using the :option`-Wd` or
|
||||
:option:`-Wa` options and leaving *warnings* to ``None``.
|
||||
|
||||
.. method:: _makeResult()
|
||||
|
||||
This method returns the instance of ``TestResult`` used by :meth:`run`.
|
||||
|
@ -1864,7 +1873,9 @@ Loading and running tests
|
|||
|
||||
stream, descriptions, verbosity
|
||||
|
||||
.. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None)
|
||||
.. versionchanged:: 3.2 Added the ``warnings`` argument
|
||||
|
||||
.. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)
|
||||
|
||||
A command-line program that runs a set of tests; this is primarily for making
|
||||
test modules conveniently executable. The simplest use for this function is to
|
||||
|
@ -1893,12 +1904,17 @@ Loading and running tests
|
|||
The ``failfast``, ``catchbreak`` and ``buffer`` parameters have the same
|
||||
effect as the same-name `command-line options`_.
|
||||
|
||||
The *warning* argument specifies the :ref:`warning filter <warning-filter>`
|
||||
that should be used while running the tests. If it's not specified, it will
|
||||
remain ``None`` if a :option:`-W` option is passed to :program:`python`,
|
||||
otherwise it will be set to ``'default'``.
|
||||
|
||||
Calling ``main`` actually returns an instance of the ``TestProgram`` class.
|
||||
This stores the result of the tests run as the ``result`` attribute.
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
The ``exit``, ``verbosity``, ``failfast``, ``catchbreak`` and ``buffer``
|
||||
parameters were added.
|
||||
The ``exit``, ``verbosity``, ``failfast``, ``catchbreak``, ``buffer``,
|
||||
and ``warnings`` parameters were added.
|
||||
|
||||
|
||||
load_tests Protocol
|
||||
|
|
|
@ -249,6 +249,8 @@ continues to increase after each operation, or else delete the previous
|
|||
entries from the warnings list before each new operation).
|
||||
|
||||
|
||||
.. _warning-ignored:
|
||||
|
||||
Updating Code For New Versions of Python
|
||||
----------------------------------------
|
||||
|
||||
|
@ -279,6 +281,9 @@ code that were not there in an older interpreter, e.g.
|
|||
developer want to be notified that your code is using a deprecated module, to a
|
||||
user this information is essentially noise and provides no benefit to them.
|
||||
|
||||
The :mod:`unittest` module has been also updated to use the ``'default'``
|
||||
filter while running tests.
|
||||
|
||||
|
||||
.. _warning-functions:
|
||||
|
||||
|
|
|
@ -67,12 +67,12 @@ class TestProgram(object):
|
|||
USAGE = USAGE_FROM_MODULE
|
||||
|
||||
# defaults for testing
|
||||
failfast = catchbreak = buffer = progName = None
|
||||
failfast = catchbreak = buffer = progName = warnings = None
|
||||
|
||||
def __init__(self, module='__main__', defaultTest=None, argv=None,
|
||||
testRunner=None, testLoader=loader.defaultTestLoader,
|
||||
exit=True, verbosity=1, failfast=None, catchbreak=None,
|
||||
buffer=None):
|
||||
buffer=None, warnings=None):
|
||||
if isinstance(module, str):
|
||||
self.module = __import__(module)
|
||||
for part in module.split('.')[1:]:
|
||||
|
@ -87,6 +87,18 @@ def __init__(self, module='__main__', defaultTest=None, argv=None,
|
|||
self.catchbreak = catchbreak
|
||||
self.verbosity = verbosity
|
||||
self.buffer = buffer
|
||||
if warnings is None and not sys.warnoptions:
|
||||
# even if DreprecationWarnings are ignored by default
|
||||
# print them anyway unless other warnings settings are
|
||||
# specified by the warnings arg or the -W python flag
|
||||
self.warnings = 'default'
|
||||
else:
|
||||
# here self.warnings is set either to the value passed
|
||||
# to the warnings args or to None.
|
||||
# If the user didn't pass a value self.warnings will
|
||||
# be None. This means that the behavior is unchanged
|
||||
# and depends on the values passed to -W.
|
||||
self.warnings = warnings
|
||||
self.defaultTest = defaultTest
|
||||
self.testRunner = testRunner
|
||||
self.testLoader = testLoader
|
||||
|
@ -220,7 +232,8 @@ def runTests(self):
|
|||
try:
|
||||
testRunner = self.testRunner(verbosity=self.verbosity,
|
||||
failfast=self.failfast,
|
||||
buffer=self.buffer)
|
||||
buffer=self.buffer,
|
||||
warnings=self.warnings)
|
||||
except TypeError:
|
||||
# didn't accept the verbosity, buffer or failfast arguments
|
||||
testRunner = self.testRunner()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
from . import result
|
||||
from .signals import registerResult
|
||||
|
@ -125,12 +126,13 @@ class TextTestRunner(object):
|
|||
resultclass = TextTestResult
|
||||
|
||||
def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
|
||||
failfast=False, buffer=False, resultclass=None):
|
||||
failfast=False, buffer=False, resultclass=None, warnings=None):
|
||||
self.stream = _WritelnDecorator(stream)
|
||||
self.descriptions = descriptions
|
||||
self.verbosity = verbosity
|
||||
self.failfast = failfast
|
||||
self.buffer = buffer
|
||||
self.warnings = warnings
|
||||
if resultclass is not None:
|
||||
self.resultclass = resultclass
|
||||
|
||||
|
@ -143,17 +145,30 @@ def run(self, test):
|
|||
registerResult(result)
|
||||
result.failfast = self.failfast
|
||||
result.buffer = self.buffer
|
||||
startTime = time.time()
|
||||
startTestRun = getattr(result, 'startTestRun', None)
|
||||
if startTestRun is not None:
|
||||
startTestRun()
|
||||
try:
|
||||
test(result)
|
||||
finally:
|
||||
stopTestRun = getattr(result, 'stopTestRun', None)
|
||||
if stopTestRun is not None:
|
||||
stopTestRun()
|
||||
stopTime = time.time()
|
||||
with warnings.catch_warnings():
|
||||
if self.warnings:
|
||||
# if self.warnings is set, use it to filter all the warnings
|
||||
warnings.simplefilter(self.warnings)
|
||||
# if the filter is 'default' or 'always', special-case the
|
||||
# warnings from the deprecated unittest methods to show them
|
||||
# no more than once per module, because they can be fairly
|
||||
# noisy. The -Wd and -Wa flags can be used to bypass this
|
||||
# only when self.warnings is None.
|
||||
if self.warnings in ['default', 'always']:
|
||||
warnings.filterwarnings('module',
|
||||
category=DeprecationWarning,
|
||||
message='Please use assert\w+ instead.')
|
||||
startTime = time.time()
|
||||
startTestRun = getattr(result, 'startTestRun', None)
|
||||
if startTestRun is not None:
|
||||
startTestRun()
|
||||
try:
|
||||
test(result)
|
||||
finally:
|
||||
stopTestRun = getattr(result, 'stopTestRun', None)
|
||||
if stopTestRun is not None:
|
||||
stopTestRun()
|
||||
stopTime = time.time()
|
||||
timeTaken = stopTime - startTime
|
||||
result.printErrors()
|
||||
if hasattr(result, 'separator2'):
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# helper module for test_runner.Test_TextTestRunner.test_warnings
|
||||
|
||||
"""
|
||||
This module has a number of tests that raise different kinds of warnings.
|
||||
When the tests are run, the warnings are caught and their messages are printed
|
||||
to stdout. This module also accepts an arg that is then passed to
|
||||
unittest.main to affect the behavior of warnings.
|
||||
Test_TextTestRunner.test_warnings executes this script with different
|
||||
combinations of warnings args and -W flags and check that the output is correct.
|
||||
See #10535.
|
||||
"""
|
||||
|
||||
import io
|
||||
import sys
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
def warnfun():
|
||||
warnings.warn('rw', RuntimeWarning)
|
||||
|
||||
class TestWarnings(unittest.TestCase):
|
||||
# unittest warnings will be printed at most once per type (max one message
|
||||
# for the fail* methods, and one for the assert* methods)
|
||||
def test_assert(self):
|
||||
self.assertEquals(2+2, 4)
|
||||
self.assertEquals(2*2, 4)
|
||||
self.assertEquals(2**2, 4)
|
||||
|
||||
def test_fail(self):
|
||||
self.failUnless(1)
|
||||
self.failUnless(True)
|
||||
|
||||
def test_other_unittest(self):
|
||||
self.assertAlmostEqual(2+2, 4)
|
||||
self.assertNotAlmostEqual(4+4, 2)
|
||||
|
||||
# these warnings are normally silenced, but they are printed in unittest
|
||||
def test_deprecation(self):
|
||||
warnings.warn('dw', DeprecationWarning)
|
||||
warnings.warn('dw', DeprecationWarning)
|
||||
warnings.warn('dw', DeprecationWarning)
|
||||
|
||||
def test_import(self):
|
||||
warnings.warn('iw', ImportWarning)
|
||||
warnings.warn('iw', ImportWarning)
|
||||
warnings.warn('iw', ImportWarning)
|
||||
|
||||
# user warnings should always be printed
|
||||
def test_warning(self):
|
||||
warnings.warn('uw')
|
||||
warnings.warn('uw')
|
||||
warnings.warn('uw')
|
||||
|
||||
# these warnings come from the same place; they will be printed
|
||||
# only once by default or three times if the 'always' filter is used
|
||||
def test_function(self):
|
||||
|
||||
warnfun()
|
||||
warnfun()
|
||||
warnfun()
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
with warnings.catch_warnings(record=True) as ws:
|
||||
# if an arg is provided pass it to unittest.main as 'warnings'
|
||||
if len(sys.argv) == 2:
|
||||
unittest.main(exit=False, warnings=sys.argv.pop())
|
||||
else:
|
||||
unittest.main(exit=False)
|
||||
|
||||
# print all the warning messages collected
|
||||
for w in ws:
|
||||
print(w.message)
|
|
@ -209,7 +209,8 @@ def __init__(self, catchbreak):
|
|||
|
||||
self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
|
||||
'verbosity': verbosity,
|
||||
'failfast': failfast})])
|
||||
'failfast': failfast,
|
||||
'warnings': None})])
|
||||
self.assertEqual(FakeRunner.runArgs, [test])
|
||||
self.assertEqual(p.result, result)
|
||||
|
||||
|
@ -222,7 +223,8 @@ def __init__(self, catchbreak):
|
|||
|
||||
self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
|
||||
'verbosity': verbosity,
|
||||
'failfast': failfast})])
|
||||
'failfast': failfast,
|
||||
'warnings': None})])
|
||||
self.assertEqual(FakeRunner.runArgs, [test])
|
||||
self.assertEqual(p.result, result)
|
||||
|
||||
|
|
|
@ -182,6 +182,27 @@ def testBufferCatchFailfast(self):
|
|||
program.parseArgs([None, opt])
|
||||
self.assertEqual(getattr(program, attr), not_none)
|
||||
|
||||
def testWarning(self):
|
||||
"""Test the warnings argument"""
|
||||
# see #10535
|
||||
class FakeTP(unittest.TestProgram):
|
||||
def parseArgs(self, *args, **kw): pass
|
||||
def runTests(self, *args, **kw): pass
|
||||
warnoptions = sys.warnoptions
|
||||
try:
|
||||
sys.warnoptions[:] = []
|
||||
# no warn options, no arg -> default
|
||||
self.assertEqual(FakeTP().warnings, 'default')
|
||||
# no warn options, w/ arg -> arg value
|
||||
self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
|
||||
sys.warnoptions[:] = ['somevalue']
|
||||
# warn options, no arg -> None
|
||||
# warn options, w/ arg -> arg value
|
||||
self.assertEqual(FakeTP().warnings, None)
|
||||
self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
|
||||
finally:
|
||||
sys.warnoptions[:] = warnoptions
|
||||
|
||||
def testRunTestsRunnerClass(self):
|
||||
program = self.program
|
||||
|
||||
|
@ -189,12 +210,14 @@ def testRunTestsRunnerClass(self):
|
|||
program.verbosity = 'verbosity'
|
||||
program.failfast = 'failfast'
|
||||
program.buffer = 'buffer'
|
||||
program.warnings = 'warnings'
|
||||
|
||||
program.runTests()
|
||||
|
||||
self.assertEqual(FakeRunner.initArgs, {'verbosity': 'verbosity',
|
||||
'failfast': 'failfast',
|
||||
'buffer': 'buffer'})
|
||||
'buffer': 'buffer',
|
||||
'warnings': 'warnings'})
|
||||
self.assertEqual(FakeRunner.test, 'test')
|
||||
self.assertIs(program.result, RESULT)
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import io
|
||||
import os
|
||||
import sys
|
||||
import pickle
|
||||
import subprocess
|
||||
|
||||
import unittest
|
||||
|
||||
|
@ -144,6 +147,7 @@ def test_init(self):
|
|||
self.assertFalse(runner.failfast)
|
||||
self.assertFalse(runner.buffer)
|
||||
self.assertEqual(runner.verbosity, 1)
|
||||
self.assertEqual(runner.warnings, None)
|
||||
self.assertTrue(runner.descriptions)
|
||||
self.assertEqual(runner.resultclass, unittest.TextTestResult)
|
||||
|
||||
|
@ -244,3 +248,57 @@ def MockResultClass(*args):
|
|||
|
||||
expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY)
|
||||
self.assertEqual(runner._makeResult(), expectedresult)
|
||||
|
||||
def test_warnings(self):
|
||||
"""
|
||||
Check that warnings argument of TextTestRunner correctly affects the
|
||||
behavior of the warnings.
|
||||
"""
|
||||
# see #10535 and the _test_warnings file for more information
|
||||
|
||||
def get_parse_out_err(p):
|
||||
return [b.splitlines() for b in p.communicate()]
|
||||
opts = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
cwd=os.path.dirname(__file__))
|
||||
ae_msg = b'Please use assertEqual instead.'
|
||||
at_msg = b'Please use assertTrue instead.'
|
||||
|
||||
# no args -> all the warnings are printed, unittest warnings only once
|
||||
p = subprocess.Popen([sys.executable, '_test_warnings.py'], **opts)
|
||||
out, err = get_parse_out_err(p)
|
||||
self.assertEqual(err[-1], b'OK')
|
||||
# check that the total number of warnings in the output is correct
|
||||
self.assertEqual(len(out), 12)
|
||||
# check that the numbers of the different kind of warnings is correct
|
||||
for msg in [b'dw', b'iw', b'uw']:
|
||||
self.assertEqual(out.count(msg), 3)
|
||||
for msg in [ae_msg, at_msg, b'rw']:
|
||||
self.assertEqual(out.count(msg), 1)
|
||||
|
||||
args_list = (
|
||||
# passing 'ignore' as warnings arg -> no warnings
|
||||
[sys.executable, '_test_warnings.py', 'ignore'],
|
||||
# -W doesn't affect the result if the arg is passed
|
||||
[sys.executable, '-Wa', '_test_warnings.py', 'ignore'],
|
||||
# -W affects the result if the arg is not passed
|
||||
[sys.executable, '-Wi', '_test_warnings.py']
|
||||
)
|
||||
# in all these cases no warnings are printed
|
||||
for args in args_list:
|
||||
p = subprocess.Popen(args, **opts)
|
||||
out, err = get_parse_out_err(p)
|
||||
self.assertEqual(err[-1], b'OK')
|
||||
self.assertEqual(len(out), 0)
|
||||
|
||||
|
||||
# passing 'always' as warnings arg -> all the warnings printed,
|
||||
# unittest warnings only once
|
||||
p = subprocess.Popen([sys.executable, '_test_warnings.py', 'always'],
|
||||
**opts)
|
||||
out, err = get_parse_out_err(p)
|
||||
self.assertEqual(err[-1], b'OK')
|
||||
self.assertEqual(len(out), 14)
|
||||
for msg in [b'dw', b'iw', b'uw', b'rw']:
|
||||
self.assertEqual(out.count(msg), 3)
|
||||
for msg in [ae_msg, at_msg]:
|
||||
self.assertEqual(out.count(msg), 1)
|
||||
|
|
Loading…
Reference in New Issue