# -*- 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 *"])