# -*- coding: utf8 -*- from __future__ import unicode_literals import sys import pytest WARNINGS_SUMMARY_HEADER = 'warnings summary' @pytest.fixture def pyfile_with_warnings(testdir, request): """ Create a test file which calls a function in a module which generates warnings. """ testdir.syspathinsert() test_name = request.function.__name__ module_name = test_name.lstrip('test_') + '_module' testdir.makepyfile(**{ module_name: ''' import warnings def foo(): warnings.warn(UserWarning("user warning")) warnings.warn(RuntimeWarning("runtime warning")) return 1 ''', test_name: ''' import {module_name} def test_func(): assert {module_name}.foo() == 1 '''.format(module_name=module_name) }) @pytest.mark.filterwarnings('always') def test_normal_flow(testdir, pyfile_with_warnings): """ Check that the warnings section is displayed, containing test node ids followed by all warnings generated by that test node. """ result = testdir.runpytest() result.stdout.fnmatch_lines([ '*== %s ==*' % WARNINGS_SUMMARY_HEADER, '*test_normal_flow.py::test_func', '*normal_flow_module.py:3: UserWarning: user warning', '* warnings.warn(UserWarning("user warning"))', '*normal_flow_module.py:4: RuntimeWarning: runtime warning', '* warnings.warn(RuntimeWarning("runtime warning"))', '* 1 passed, 2 warnings*', ]) assert result.stdout.str().count('test_normal_flow.py::test_func') == 1 @pytest.mark.filterwarnings('always') def test_setup_teardown_warnings(testdir, pyfile_with_warnings): testdir.makepyfile(''' import warnings import pytest @pytest.fixture def fix(): warnings.warn(UserWarning("warning during setup")) yield warnings.warn(UserWarning("warning during teardown")) def test_func(fix): pass ''') result = testdir.runpytest() result.stdout.fnmatch_lines([ '*== %s ==*' % WARNINGS_SUMMARY_HEADER, '*test_setup_teardown_warnings.py:6: UserWarning: warning during setup', '*warnings.warn(UserWarning("warning during setup"))', '*test_setup_teardown_warnings.py:8: UserWarning: warning during teardown', '*warnings.warn(UserWarning("warning during teardown"))', '* 1 passed, 2 warnings*', ]) @pytest.mark.parametrize('method', ['cmdline', 'ini']) def test_as_errors(testdir, pyfile_with_warnings, method): args = ('-W', 'error') if method == 'cmdline' else () if method == 'ini': testdir.makeini(''' [pytest] filterwarnings= error ''') result = testdir.runpytest(*args) result.stdout.fnmatch_lines([ 'E UserWarning: user warning', 'as_errors_module.py:3: UserWarning', '* 1 failed in *', ]) @pytest.mark.parametrize('method', ['cmdline', 'ini']) def test_ignore(testdir, pyfile_with_warnings, method): args = ('-W', 'ignore') if method == 'cmdline' else () if method == 'ini': testdir.makeini(''' [pytest] filterwarnings= ignore ''') result = testdir.runpytest(*args) result.stdout.fnmatch_lines([ '* 1 passed in *', ]) assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() @pytest.mark.skipif(sys.version_info < (3, 0), reason='warnings message is unicode is ok in python3') @pytest.mark.filterwarnings('always') def test_unicode(testdir, pyfile_with_warnings): testdir.makepyfile(''' # -*- coding: utf8 -*- import warnings import pytest @pytest.fixture def fix(): warnings.warn(u"测试") yield def test_func(fix): pass ''') result = testdir.runpytest() result.stdout.fnmatch_lines([ '*== %s ==*' % WARNINGS_SUMMARY_HEADER, '*test_unicode.py:8: UserWarning: \u6d4b\u8bd5*', '* 1 passed, 1 warnings*', ]) @pytest.mark.skipif(sys.version_info >= (3, 0), reason='warnings message is broken as it is not str instance') def test_py2_unicode(testdir, pyfile_with_warnings): if getattr(sys, "pypy_version_info", ())[:2] == (5, 9) and sys.platform.startswith('win'): pytest.xfail("fails with unicode error on PyPy2 5.9 and Windows (#2905)") testdir.makepyfile(''' # -*- coding: utf8 -*- import warnings import pytest @pytest.fixture def fix(): warnings.warn(u"测试") yield @pytest.mark.filterwarnings('always') def test_func(fix): pass ''') result = testdir.runpytest() result.stdout.fnmatch_lines([ '*== %s ==*' % WARNINGS_SUMMARY_HEADER, '*test_py2_unicode.py:8: UserWarning: \\u6d4b\\u8bd5', '*warnings.warn(u"\u6d4b\u8bd5")', '*warnings.py:*: UnicodeWarning: Warning is using unicode non*', '* 1 passed, 2 warnings*', ]) def test_py2_unicode_ascii(testdir): """Ensure that our warning about 'unicode warnings containing non-ascii messages' does not trigger with ascii-convertible messages""" testdir.makeini('[pytest]') testdir.makepyfile(''' import pytest import warnings @pytest.mark.filterwarnings('always') def test_func(): warnings.warn(u"hello") ''') result = testdir.runpytest() result.stdout.fnmatch_lines([ '*== %s ==*' % WARNINGS_SUMMARY_HEADER, '*warnings.warn(u"hello")', '* 1 passed, 1 warnings in*' ]) def test_works_with_filterwarnings(testdir): """Ensure our warnings capture does not mess with pre-installed filters (#2430).""" testdir.makepyfile(''' import warnings class MyWarning(Warning): pass warnings.filterwarnings("error", category=MyWarning) class TestWarnings(object): def test_my_warning(self): try: warnings.warn(MyWarning("warn!")) assert False except MyWarning: assert True ''') result = testdir.runpytest() result.stdout.fnmatch_lines([ '*== 1 passed in *', ]) @pytest.mark.parametrize('default_config', ['ini', 'cmdline']) def test_filterwarnings_mark(testdir, default_config): """ Test ``filterwarnings`` mark works and takes precedence over command line and ini options. """ if default_config == 'ini': testdir.makeini(""" [pytest] filterwarnings = always """) testdir.makepyfile(""" import warnings import pytest @pytest.mark.filterwarnings('ignore::RuntimeWarning') def test_ignore_runtime_warning(): warnings.warn(RuntimeWarning()) @pytest.mark.filterwarnings('error') def test_warning_error(): warnings.warn(RuntimeWarning()) def test_show_warning(): warnings.warn(RuntimeWarning()) """) result = testdir.runpytest('-W always' if default_config == 'cmdline' else '') result.stdout.fnmatch_lines(['*= 1 failed, 2 passed, 1 warnings in *']) def test_non_string_warning_argument(testdir): """Non-str argument passed to warning breaks pytest (#2956)""" testdir.makepyfile(""" import warnings import pytest def test(): warnings.warn(UserWarning(1, u'foo')) """) result = testdir.runpytest('-W', 'always') result.stdout.fnmatch_lines(['*= 1 passed, 1 warnings in *'])