| # -*- coding: utf-8 -*- |
| import os |
| import sys |
| from textwrap import dedent |
| |
| import _pytest._code |
| import pytest |
| from _pytest.main import EXIT_NOTESTSCOLLECTED |
| from _pytest.nodes import Collector |
| |
| ignore_parametrized_marks = pytest.mark.filterwarnings( |
| "ignore:Applying marks directly to parameters" |
| ) |
| |
| |
| class TestModule(object): |
| |
| def test_failing_import(self, testdir): |
| modcol = testdir.getmodulecol("import alksdjalskdjalkjals") |
| pytest.raises(Collector.CollectError, modcol.collect) |
| |
| def test_import_duplicate(self, testdir): |
| a = testdir.mkdir("a") |
| b = testdir.mkdir("b") |
| p = a.ensure("test_whatever.py") |
| p.pyimport() |
| del sys.modules["test_whatever"] |
| b.ensure("test_whatever.py") |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines( |
| [ |
| "*import*mismatch*", |
| "*imported*test_whatever*", |
| "*%s*" % a.join("test_whatever.py"), |
| "*not the same*", |
| "*%s*" % b.join("test_whatever.py"), |
| "*HINT*", |
| ] |
| ) |
| |
| def test_import_prepend_append(self, testdir, monkeypatch): |
| syspath = list(sys.path) |
| monkeypatch.setattr(sys, "path", syspath) |
| root1 = testdir.mkdir("root1") |
| root2 = testdir.mkdir("root2") |
| root1.ensure("x456.py") |
| root2.ensure("x456.py") |
| p = root2.join("test_x456.py") |
| monkeypatch.syspath_prepend(str(root1)) |
| p.write( |
| dedent( |
| """\ |
| import x456 |
| def test(): |
| assert x456.__file__.startswith(%r) |
| """ |
| % str(root2) |
| ) |
| ) |
| with root2.as_cwd(): |
| reprec = testdir.inline_run("--import-mode=append") |
| reprec.assertoutcome(passed=0, failed=1) |
| reprec = testdir.inline_run() |
| reprec.assertoutcome(passed=1) |
| |
| def test_syntax_error_in_module(self, testdir): |
| modcol = testdir.getmodulecol("this is a syntax error") |
| pytest.raises(modcol.CollectError, modcol.collect) |
| pytest.raises(modcol.CollectError, modcol.collect) |
| |
| def test_module_considers_pluginmanager_at_import(self, testdir): |
| modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',") |
| pytest.raises(ImportError, lambda: modcol.obj) |
| |
| def test_invalid_test_module_name(self, testdir): |
| a = testdir.mkdir("a") |
| a.ensure("test_one.part1.py") |
| result = testdir.runpytest("-rw") |
| result.stdout.fnmatch_lines( |
| [ |
| "ImportError while importing test module*test_one.part1*", |
| "Hint: make sure your test modules/packages have valid Python names.", |
| ] |
| ) |
| |
| @pytest.mark.parametrize("verbose", [0, 1, 2]) |
| def test_show_traceback_import_error(self, testdir, verbose): |
| """Import errors when collecting modules should display the traceback (#1976). |
| |
| With low verbosity we omit pytest and internal modules, otherwise show all traceback entries. |
| """ |
| testdir.makepyfile( |
| foo_traceback_import_error=""" |
| from bar_traceback_import_error import NOT_AVAILABLE |
| """, |
| bar_traceback_import_error="", |
| ) |
| testdir.makepyfile( |
| """ |
| import foo_traceback_import_error |
| """ |
| ) |
| args = ("-v",) * verbose |
| result = testdir.runpytest(*args) |
| result.stdout.fnmatch_lines( |
| [ |
| "ImportError while importing test module*", |
| "Traceback:", |
| "*from bar_traceback_import_error import NOT_AVAILABLE", |
| "*cannot import name *NOT_AVAILABLE*", |
| ] |
| ) |
| assert result.ret == 2 |
| |
| stdout = result.stdout.str() |
| for name in ("_pytest", os.path.join("py", "_path")): |
| if verbose == 2: |
| assert name in stdout |
| else: |
| assert name not in stdout |
| |
| def test_show_traceback_import_error_unicode(self, testdir): |
| """Check test modules collected which raise ImportError with unicode messages |
| are handled properly (#2336). |
| """ |
| testdir.makepyfile( |
| u""" |
| # -*- coding: utf-8 -*- |
| raise ImportError(u'Something bad happened ☺') |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines( |
| [ |
| "ImportError while importing test module*", |
| "Traceback:", |
| "*raise ImportError*Something bad happened*", |
| ] |
| ) |
| assert result.ret == 2 |
| |
| |
| class TestClass(object): |
| |
| def test_class_with_init_warning(self, testdir): |
| testdir.makepyfile( |
| """ |
| class TestClass1(object): |
| def __init__(self): |
| pass |
| """ |
| ) |
| result = testdir.runpytest("-rw") |
| result.stdout.fnmatch_lines( |
| [ |
| "*cannot collect test class 'TestClass1' because it has a __init__ constructor" |
| ] |
| ) |
| |
| def test_class_subclassobject(self, testdir): |
| testdir.getmodulecol( |
| """ |
| class test(object): |
| pass |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines(["*collected 0*"]) |
| |
| def test_static_method(self, testdir): |
| """Support for collecting staticmethod tests (#2528, #2699)""" |
| testdir.getmodulecol( |
| """ |
| import pytest |
| class Test(object): |
| @staticmethod |
| def test_something(): |
| pass |
| |
| @pytest.fixture |
| def fix(self): |
| return 1 |
| |
| @staticmethod |
| def test_fix(fix): |
| assert fix == 1 |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines(["*collected 2 items*", "*2 passed in*"]) |
| |
| def test_setup_teardown_class_as_classmethod(self, testdir): |
| testdir.makepyfile( |
| test_mod1=""" |
| class TestClassMethod(object): |
| @classmethod |
| def setup_class(cls): |
| pass |
| def test_1(self): |
| pass |
| @classmethod |
| def teardown_class(cls): |
| pass |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines(["*1 passed*"]) |
| |
| def test_issue1035_obj_has_getattr(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| class Chameleon(object): |
| def __getattr__(self, name): |
| return True |
| chameleon = Chameleon() |
| """ |
| ) |
| colitems = modcol.collect() |
| assert len(colitems) == 0 |
| |
| def test_issue1579_namedtuple(self, testdir): |
| testdir.makepyfile( |
| """ |
| import collections |
| |
| TestCase = collections.namedtuple('TestCase', ['a']) |
| """ |
| ) |
| result = testdir.runpytest("-rw") |
| result.stdout.fnmatch_lines( |
| "*cannot collect test class 'TestCase' " |
| "because it has a __new__ constructor*" |
| ) |
| |
| def test_issue2234_property(self, testdir): |
| testdir.makepyfile( |
| """ |
| class TestCase(object): |
| @property |
| def prop(self): |
| raise NotImplementedError() |
| """ |
| ) |
| result = testdir.runpytest() |
| assert result.ret == EXIT_NOTESTSCOLLECTED |
| |
| |
| class TestGenerator(object): |
| |
| def test_generative_functions(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| def func1(arg, arg2): |
| assert arg == arg2 |
| |
| def test_gen(): |
| yield func1, 17, 3*5 |
| yield func1, 42, 6*7 |
| """ |
| ) |
| colitems = modcol.collect() |
| assert len(colitems) == 1 |
| gencol = colitems[0] |
| assert isinstance(gencol, pytest.Generator) |
| gencolitems = gencol.collect() |
| assert len(gencolitems) == 2 |
| assert isinstance(gencolitems[0], pytest.Function) |
| assert isinstance(gencolitems[1], pytest.Function) |
| assert gencolitems[0].name == "[0]" |
| assert gencolitems[0].obj.__name__ == "func1" |
| |
| def test_generative_methods(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| def func1(arg, arg2): |
| assert arg == arg2 |
| class TestGenMethods(object): |
| def test_gen(self): |
| yield func1, 17, 3*5 |
| yield func1, 42, 6*7 |
| """ |
| ) |
| gencol = modcol.collect()[0].collect()[0].collect()[0] |
| assert isinstance(gencol, pytest.Generator) |
| gencolitems = gencol.collect() |
| assert len(gencolitems) == 2 |
| assert isinstance(gencolitems[0], pytest.Function) |
| assert isinstance(gencolitems[1], pytest.Function) |
| assert gencolitems[0].name == "[0]" |
| assert gencolitems[0].obj.__name__ == "func1" |
| |
| def test_generative_functions_with_explicit_names(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| def func1(arg, arg2): |
| assert arg == arg2 |
| |
| def test_gen(): |
| yield "seventeen", func1, 17, 3*5 |
| yield "fortytwo", func1, 42, 6*7 |
| """ |
| ) |
| colitems = modcol.collect() |
| assert len(colitems) == 1 |
| gencol = colitems[0] |
| assert isinstance(gencol, pytest.Generator) |
| gencolitems = gencol.collect() |
| assert len(gencolitems) == 2 |
| assert isinstance(gencolitems[0], pytest.Function) |
| assert isinstance(gencolitems[1], pytest.Function) |
| assert gencolitems[0].name == "['seventeen']" |
| assert gencolitems[0].obj.__name__ == "func1" |
| assert gencolitems[1].name == "['fortytwo']" |
| assert gencolitems[1].obj.__name__ == "func1" |
| |
| def test_generative_functions_unique_explicit_names(self, testdir): |
| # generative |
| modcol = testdir.getmodulecol( |
| """ |
| def func(): pass |
| def test_gen(): |
| yield "name", func |
| yield "name", func |
| """ |
| ) |
| colitems = modcol.collect() |
| assert len(colitems) == 1 |
| gencol = colitems[0] |
| assert isinstance(gencol, pytest.Generator) |
| pytest.raises(ValueError, "gencol.collect()") |
| |
| def test_generative_methods_with_explicit_names(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| def func1(arg, arg2): |
| assert arg == arg2 |
| class TestGenMethods(object): |
| def test_gen(self): |
| yield "m1", func1, 17, 3*5 |
| yield "m2", func1, 42, 6*7 |
| """ |
| ) |
| gencol = modcol.collect()[0].collect()[0].collect()[0] |
| assert isinstance(gencol, pytest.Generator) |
| gencolitems = gencol.collect() |
| assert len(gencolitems) == 2 |
| assert isinstance(gencolitems[0], pytest.Function) |
| assert isinstance(gencolitems[1], pytest.Function) |
| assert gencolitems[0].name == "['m1']" |
| assert gencolitems[0].obj.__name__ == "func1" |
| assert gencolitems[1].name == "['m2']" |
| assert gencolitems[1].obj.__name__ == "func1" |
| |
| def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir): |
| o = testdir.makepyfile( |
| """ |
| from __future__ import print_function |
| def test_generative_order_of_execution(): |
| import py, pytest |
| test_list = [] |
| expected_list = list(range(6)) |
| |
| def list_append(item): |
| test_list.append(item) |
| |
| def assert_order_of_execution(): |
| print('expected order', expected_list) |
| print('but got ', test_list) |
| assert test_list == expected_list |
| |
| for i in expected_list: |
| yield list_append, i |
| yield assert_order_of_execution |
| """ |
| ) |
| reprec = testdir.inline_run(o) |
| passed, skipped, failed = reprec.countoutcomes() |
| assert passed == 7 |
| assert not skipped and not failed |
| |
| def test_order_of_execution_generator_different_codeline(self, testdir): |
| o = testdir.makepyfile( |
| """ |
| from __future__ import print_function |
| def test_generative_tests_different_codeline(): |
| import py, pytest |
| test_list = [] |
| expected_list = list(range(3)) |
| |
| def list_append_2(): |
| test_list.append(2) |
| |
| def list_append_1(): |
| test_list.append(1) |
| |
| def list_append_0(): |
| test_list.append(0) |
| |
| def assert_order_of_execution(): |
| print('expected order', expected_list) |
| print('but got ', test_list) |
| assert test_list == expected_list |
| |
| yield list_append_0 |
| yield list_append_1 |
| yield list_append_2 |
| yield assert_order_of_execution |
| """ |
| ) |
| reprec = testdir.inline_run(o) |
| passed, skipped, failed = reprec.countoutcomes() |
| assert passed == 4 |
| assert not skipped and not failed |
| |
| def test_setupstate_is_preserved_134(self, testdir): |
| # yield-based tests are messy wrt to setupstate because |
| # during collection they already invoke setup functions |
| # and then again when they are run. For now, we want to make sure |
| # that the old 1.3.4 behaviour is preserved such that all |
| # yielded functions all share the same "self" instance that |
| # has been used during collection. |
| o = testdir.makepyfile( |
| """ |
| setuplist = [] |
| class TestClass(object): |
| def setup_method(self, func): |
| #print "setup_method", self, func |
| setuplist.append(self) |
| self.init = 42 |
| |
| def teardown_method(self, func): |
| self.init = None |
| |
| def test_func1(self): |
| pass |
| |
| def test_func2(self): |
| yield self.func2 |
| yield self.func2 |
| |
| def func2(self): |
| assert self.init |
| |
| def test_setuplist(): |
| # once for test_func2 during collection |
| # once for test_func1 during test run |
| # once for test_func2 during test run |
| #print setuplist |
| assert len(setuplist) == 3, len(setuplist) |
| assert setuplist[0] == setuplist[2], setuplist |
| assert setuplist[1] != setuplist[2], setuplist |
| """ |
| ) |
| reprec = testdir.inline_run(o, "-v") |
| passed, skipped, failed = reprec.countoutcomes() |
| assert passed == 4 |
| assert not skipped and not failed |
| |
| |
| class TestFunction(object): |
| |
| def test_getmodulecollector(self, testdir): |
| item = testdir.getitem("def test_func(): pass") |
| modcol = item.getparent(pytest.Module) |
| assert isinstance(modcol, pytest.Module) |
| assert hasattr(modcol.obj, "test_func") |
| |
| def test_function_as_object_instance_ignored(self, testdir): |
| testdir.makepyfile( |
| """ |
| class A(object): |
| def __call__(self, tmpdir): |
| 0/0 |
| |
| test_a = A() |
| """ |
| ) |
| reprec = testdir.inline_run() |
| reprec.assertoutcome() |
| |
| def test_function_equality(self, testdir, tmpdir): |
| from _pytest.fixtures import FixtureManager |
| |
| config = testdir.parseconfigure() |
| session = testdir.Session(config) |
| session._fixturemanager = FixtureManager(session) |
| |
| def func1(): |
| pass |
| |
| def func2(): |
| pass |
| |
| f1 = pytest.Function( |
| name="name", parent=session, config=config, args=(1,), callobj=func1 |
| ) |
| assert f1 == f1 |
| f2 = pytest.Function(name="name", config=config, callobj=func2, parent=session) |
| assert f1 != f2 |
| |
| def test_issue197_parametrize_emptyset(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| @pytest.mark.parametrize('arg', []) |
| def test_function(arg): |
| pass |
| """ |
| ) |
| reprec = testdir.inline_run() |
| reprec.assertoutcome(skipped=1) |
| |
| def test_single_tuple_unwraps_values(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| @pytest.mark.parametrize(('arg',), [(1,)]) |
| def test_function(arg): |
| assert arg == 1 |
| """ |
| ) |
| reprec = testdir.inline_run() |
| reprec.assertoutcome(passed=1) |
| |
| def test_issue213_parametrize_value_no_equal(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| class A(object): |
| def __eq__(self, other): |
| raise ValueError("not possible") |
| @pytest.mark.parametrize('arg', [A()]) |
| def test_function(arg): |
| assert arg.__class__.__name__ == "A" |
| """ |
| ) |
| reprec = testdir.inline_run("--fulltrace") |
| reprec.assertoutcome(passed=1) |
| |
| def test_parametrize_with_non_hashable_values(self, testdir): |
| """Test parametrization with non-hashable values.""" |
| testdir.makepyfile( |
| """ |
| archival_mapping = { |
| '1.0': {'tag': '1.0'}, |
| '1.2.2a1': {'tag': 'release-1.2.2a1'}, |
| } |
| |
| import pytest |
| @pytest.mark.parametrize('key value'.split(), |
| archival_mapping.items()) |
| def test_archival_to_version(key, value): |
| assert key in archival_mapping |
| assert value == archival_mapping[key] |
| """ |
| ) |
| rec = testdir.inline_run() |
| rec.assertoutcome(passed=2) |
| |
| def test_parametrize_with_non_hashable_values_indirect(self, testdir): |
| """Test parametrization with non-hashable values with indirect parametrization.""" |
| testdir.makepyfile( |
| """ |
| archival_mapping = { |
| '1.0': {'tag': '1.0'}, |
| '1.2.2a1': {'tag': 'release-1.2.2a1'}, |
| } |
| |
| import pytest |
| |
| @pytest.fixture |
| def key(request): |
| return request.param |
| |
| @pytest.fixture |
| def value(request): |
| return request.param |
| |
| @pytest.mark.parametrize('key value'.split(), |
| archival_mapping.items(), indirect=True) |
| def test_archival_to_version(key, value): |
| assert key in archival_mapping |
| assert value == archival_mapping[key] |
| """ |
| ) |
| rec = testdir.inline_run() |
| rec.assertoutcome(passed=2) |
| |
| def test_parametrize_overrides_fixture(self, testdir): |
| """Test parametrization when parameter overrides existing fixture with same name.""" |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| @pytest.fixture |
| def value(): |
| return 'value' |
| |
| @pytest.mark.parametrize('value', |
| ['overridden']) |
| def test_overridden_via_param(value): |
| assert value == 'overridden' |
| |
| @pytest.mark.parametrize('somevalue', ['overridden']) |
| def test_not_overridden(value, somevalue): |
| assert value == 'value' |
| assert somevalue == 'overridden' |
| |
| @pytest.mark.parametrize('other,value', [('foo', 'overridden')]) |
| def test_overridden_via_multiparam(other, value): |
| assert other == 'foo' |
| assert value == 'overridden' |
| """ |
| ) |
| rec = testdir.inline_run() |
| rec.assertoutcome(passed=3) |
| |
| def test_parametrize_overrides_parametrized_fixture(self, testdir): |
| """Test parametrization when parameter overrides existing parametrized fixture with same name.""" |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| @pytest.fixture(params=[1, 2]) |
| def value(request): |
| return request.param |
| |
| @pytest.mark.parametrize('value', |
| ['overridden']) |
| def test_overridden_via_param(value): |
| assert value == 'overridden' |
| """ |
| ) |
| rec = testdir.inline_run() |
| rec.assertoutcome(passed=1) |
| |
| @ignore_parametrized_marks |
| def test_parametrize_with_mark(self, testdir): |
| items = testdir.getitems( |
| """ |
| import pytest |
| @pytest.mark.foo |
| @pytest.mark.parametrize('arg', [ |
| 1, |
| pytest.mark.bar(pytest.mark.baz(2)) |
| ]) |
| def test_function(arg): |
| pass |
| """ |
| ) |
| keywords = [item.keywords for item in items] |
| assert ( |
| "foo" in keywords[0] |
| and "bar" not in keywords[0] |
| and "baz" not in keywords[0] |
| ) |
| assert "foo" in keywords[1] and "bar" in keywords[1] and "baz" in keywords[1] |
| |
| def test_function_equality_with_callspec(self, testdir, tmpdir): |
| items = testdir.getitems( |
| """ |
| import pytest |
| @pytest.mark.parametrize('arg', [1,2]) |
| def test_function(arg): |
| pass |
| """ |
| ) |
| assert items[0] != items[1] |
| assert not (items[0] == items[1]) |
| |
| def test_pyfunc_call(self, testdir): |
| item = testdir.getitem("def test_func(): raise ValueError") |
| config = item.config |
| |
| class MyPlugin1(object): |
| |
| def pytest_pyfunc_call(self, pyfuncitem): |
| raise ValueError |
| |
| class MyPlugin2(object): |
| |
| def pytest_pyfunc_call(self, pyfuncitem): |
| return True |
| |
| config.pluginmanager.register(MyPlugin1()) |
| config.pluginmanager.register(MyPlugin2()) |
| config.hook.pytest_runtest_setup(item=item) |
| config.hook.pytest_pyfunc_call(pyfuncitem=item) |
| |
| def test_multiple_parametrize(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| import pytest |
| @pytest.mark.parametrize('x', [0, 1]) |
| @pytest.mark.parametrize('y', [2, 3]) |
| def test1(x, y): |
| pass |
| """ |
| ) |
| colitems = modcol.collect() |
| assert colitems[0].name == "test1[2-0]" |
| assert colitems[1].name == "test1[2-1]" |
| assert colitems[2].name == "test1[3-0]" |
| assert colitems[3].name == "test1[3-1]" |
| |
| def test_issue751_multiple_parametrize_with_ids(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| import pytest |
| @pytest.mark.parametrize('x', [0], ids=['c']) |
| @pytest.mark.parametrize('y', [0, 1], ids=['a', 'b']) |
| class Test(object): |
| def test1(self, x, y): |
| pass |
| def test2(self, x, y): |
| pass |
| """ |
| ) |
| colitems = modcol.collect()[0].collect()[0].collect() |
| assert colitems[0].name == "test1[a-c]" |
| assert colitems[1].name == "test1[b-c]" |
| assert colitems[2].name == "test2[a-c]" |
| assert colitems[3].name == "test2[b-c]" |
| |
| @ignore_parametrized_marks |
| def test_parametrize_skipif(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| m = pytest.mark.skipif('True') |
| |
| @pytest.mark.parametrize('x', [0, 1, m(2)]) |
| def test_skip_if(x): |
| assert x < 2 |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines("* 2 passed, 1 skipped in *") |
| |
| @ignore_parametrized_marks |
| def test_parametrize_skip(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| m = pytest.mark.skip('') |
| |
| @pytest.mark.parametrize('x', [0, 1, m(2)]) |
| def test_skip(x): |
| assert x < 2 |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines("* 2 passed, 1 skipped in *") |
| |
| @ignore_parametrized_marks |
| def test_parametrize_skipif_no_skip(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| m = pytest.mark.skipif('False') |
| |
| @pytest.mark.parametrize('x', [0, 1, m(2)]) |
| def test_skipif_no_skip(x): |
| assert x < 2 |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines("* 1 failed, 2 passed in *") |
| |
| @ignore_parametrized_marks |
| def test_parametrize_xfail(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| m = pytest.mark.xfail('True') |
| |
| @pytest.mark.parametrize('x', [0, 1, m(2)]) |
| def test_xfail(x): |
| assert x < 2 |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines("* 2 passed, 1 xfailed in *") |
| |
| @ignore_parametrized_marks |
| def test_parametrize_passed(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| m = pytest.mark.xfail('True') |
| |
| @pytest.mark.parametrize('x', [0, 1, m(2)]) |
| def test_xfail(x): |
| pass |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines("* 2 passed, 1 xpassed in *") |
| |
| @ignore_parametrized_marks |
| def test_parametrize_xfail_passed(self, testdir): |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| m = pytest.mark.xfail('False') |
| |
| @pytest.mark.parametrize('x', [0, 1, m(2)]) |
| def test_passed(x): |
| pass |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines("* 3 passed in *") |
| |
| def test_function_original_name(self, testdir): |
| items = testdir.getitems( |
| """ |
| import pytest |
| @pytest.mark.parametrize('arg', [1,2]) |
| def test_func(arg): |
| pass |
| """ |
| ) |
| assert [x.originalname for x in items] == ["test_func", "test_func"] |
| |
| |
| class TestSorting(object): |
| |
| def test_check_equality(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| def test_pass(): pass |
| def test_fail(): assert 0 |
| """ |
| ) |
| fn1 = testdir.collect_by_name(modcol, "test_pass") |
| assert isinstance(fn1, pytest.Function) |
| fn2 = testdir.collect_by_name(modcol, "test_pass") |
| assert isinstance(fn2, pytest.Function) |
| |
| assert fn1 == fn2 |
| assert fn1 != modcol |
| if sys.version_info < (3, 0): |
| assert cmp(fn1, fn2) == 0 # NOQA |
| assert hash(fn1) == hash(fn2) |
| |
| fn3 = testdir.collect_by_name(modcol, "test_fail") |
| assert isinstance(fn3, pytest.Function) |
| assert not (fn1 == fn3) |
| assert fn1 != fn3 |
| |
| for fn in fn1, fn2, fn3: |
| assert fn != 3 |
| assert fn != modcol |
| assert fn != [1, 2, 3] |
| assert [1, 2, 3] != fn |
| assert modcol != fn |
| |
| def test_allow_sane_sorting_for_decorators(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| def dec(f): |
| g = lambda: f(2) |
| g.place_as = f |
| return g |
| |
| |
| def test_b(y): |
| pass |
| test_b = dec(test_b) |
| |
| def test_a(y): |
| pass |
| test_a = dec(test_a) |
| """ |
| ) |
| colitems = modcol.collect() |
| assert len(colitems) == 2 |
| assert [item.name for item in colitems] == ["test_b", "test_a"] |
| |
| |
| class TestConftestCustomization(object): |
| |
| def test_pytest_pycollect_module(self, testdir): |
| testdir.makeconftest( |
| """ |
| import pytest |
| class MyModule(pytest.Module): |
| pass |
| def pytest_pycollect_makemodule(path, parent): |
| if path.basename == "test_xyz.py": |
| return MyModule(path, parent) |
| """ |
| ) |
| testdir.makepyfile("def test_some(): pass") |
| testdir.makepyfile(test_xyz="def test_func(): pass") |
| result = testdir.runpytest("--collect-only") |
| result.stdout.fnmatch_lines(["*<Module*test_pytest*", "*<MyModule*xyz*"]) |
| |
| def test_customized_pymakemodule_issue205_subdir(self, testdir): |
| b = testdir.mkdir("a").mkdir("b") |
| b.join("conftest.py").write( |
| _pytest._code.Source( |
| """ |
| import pytest |
| @pytest.hookimpl(hookwrapper=True) |
| def pytest_pycollect_makemodule(): |
| outcome = yield |
| mod = outcome.get_result() |
| mod.obj.hello = "world" |
| """ |
| ) |
| ) |
| b.join("test_module.py").write( |
| _pytest._code.Source( |
| """ |
| def test_hello(): |
| assert hello == "world" |
| """ |
| ) |
| ) |
| reprec = testdir.inline_run() |
| reprec.assertoutcome(passed=1) |
| |
| def test_customized_pymakeitem(self, testdir): |
| b = testdir.mkdir("a").mkdir("b") |
| b.join("conftest.py").write( |
| _pytest._code.Source( |
| """ |
| import pytest |
| @pytest.hookimpl(hookwrapper=True) |
| def pytest_pycollect_makeitem(): |
| outcome = yield |
| if outcome.excinfo is None: |
| result = outcome.get_result() |
| if result: |
| for func in result: |
| func._some123 = "world" |
| """ |
| ) |
| ) |
| b.join("test_module.py").write( |
| _pytest._code.Source( |
| """ |
| import pytest |
| |
| @pytest.fixture() |
| def obj(request): |
| return request.node._some123 |
| def test_hello(obj): |
| assert obj == "world" |
| """ |
| ) |
| ) |
| reprec = testdir.inline_run() |
| reprec.assertoutcome(passed=1) |
| |
| def test_pytest_pycollect_makeitem(self, testdir): |
| testdir.makeconftest( |
| """ |
| import pytest |
| class MyFunction(pytest.Function): |
| pass |
| def pytest_pycollect_makeitem(collector, name, obj): |
| if name == "some": |
| return MyFunction(name, collector) |
| """ |
| ) |
| testdir.makepyfile("def some(): pass") |
| result = testdir.runpytest("--collect-only") |
| result.stdout.fnmatch_lines(["*MyFunction*some*"]) |
| |
| def test_makeitem_non_underscore(self, testdir, monkeypatch): |
| modcol = testdir.getmodulecol("def _hello(): pass") |
| values = [] |
| monkeypatch.setattr( |
| pytest.Module, "makeitem", lambda self, name, obj: values.append(name) |
| ) |
| values = modcol.collect() |
| assert "_hello" not in values |
| |
| def test_issue2369_collect_module_fileext(self, testdir): |
| """Ensure we can collect files with weird file extensions as Python |
| modules (#2369)""" |
| # We'll implement a little finder and loader to import files containing |
| # Python source code whose file extension is ".narf". |
| testdir.makeconftest( |
| """ |
| import sys, os, imp |
| from _pytest.python import Module |
| |
| class Loader(object): |
| def load_module(self, name): |
| return imp.load_source(name, name + ".narf") |
| class Finder(object): |
| def find_module(self, name, path=None): |
| if os.path.exists(name + ".narf"): |
| return Loader() |
| sys.meta_path.append(Finder()) |
| |
| def pytest_collect_file(path, parent): |
| if path.ext == ".narf": |
| return Module(path, parent)""" |
| ) |
| testdir.makefile( |
| ".narf", |
| """ |
| def test_something(): |
| assert 1 + 1 == 2""", |
| ) |
| # Use runpytest_subprocess, since we're futzing with sys.meta_path. |
| result = testdir.runpytest_subprocess() |
| result.stdout.fnmatch_lines("*1 passed*") |
| |
| |
| def test_setup_only_available_in_subdir(testdir): |
| sub1 = testdir.mkpydir("sub1") |
| sub2 = testdir.mkpydir("sub2") |
| sub1.join("conftest.py").write( |
| _pytest._code.Source( |
| """ |
| import pytest |
| def pytest_runtest_setup(item): |
| assert item.fspath.purebasename == "test_in_sub1" |
| def pytest_runtest_call(item): |
| assert item.fspath.purebasename == "test_in_sub1" |
| def pytest_runtest_teardown(item): |
| assert item.fspath.purebasename == "test_in_sub1" |
| """ |
| ) |
| ) |
| sub2.join("conftest.py").write( |
| _pytest._code.Source( |
| """ |
| import pytest |
| def pytest_runtest_setup(item): |
| assert item.fspath.purebasename == "test_in_sub2" |
| def pytest_runtest_call(item): |
| assert item.fspath.purebasename == "test_in_sub2" |
| def pytest_runtest_teardown(item): |
| assert item.fspath.purebasename == "test_in_sub2" |
| """ |
| ) |
| ) |
| sub1.join("test_in_sub1.py").write("def test_1(): pass") |
| sub2.join("test_in_sub2.py").write("def test_2(): pass") |
| result = testdir.runpytest("-v", "-s") |
| result.assert_outcomes(passed=2) |
| |
| |
| def test_modulecol_roundtrip(testdir): |
| modcol = testdir.getmodulecol("pass", withinit=True) |
| trail = modcol.nodeid |
| newcol = modcol.session.perform_collect([trail], genitems=0)[0] |
| assert modcol.name == newcol.name |
| |
| |
| class TestTracebackCutting(object): |
| |
| def test_skip_simple(self): |
| excinfo = pytest.raises(pytest.skip.Exception, 'pytest.skip("xxx")') |
| assert excinfo.traceback[-1].frame.code.name == "skip" |
| assert excinfo.traceback[-1].ishidden() |
| |
| def test_traceback_argsetup(self, testdir): |
| testdir.makeconftest( |
| """ |
| import pytest |
| |
| @pytest.fixture |
| def hello(request): |
| raise ValueError("xyz") |
| """ |
| ) |
| p = testdir.makepyfile("def test(hello): pass") |
| result = testdir.runpytest(p) |
| assert result.ret != 0 |
| out = result.stdout.str() |
| assert "xyz" in out |
| assert "conftest.py:5: ValueError" in out |
| numentries = out.count("_ _ _") # separator for traceback entries |
| assert numentries == 0 |
| |
| result = testdir.runpytest("--fulltrace", p) |
| out = result.stdout.str() |
| assert "conftest.py:5: ValueError" in out |
| numentries = out.count("_ _ _ _") # separator for traceback entries |
| assert numentries > 3 |
| |
| def test_traceback_error_during_import(self, testdir): |
| testdir.makepyfile( |
| """ |
| x = 1 |
| x = 2 |
| x = 17 |
| asd |
| """ |
| ) |
| result = testdir.runpytest() |
| assert result.ret != 0 |
| out = result.stdout.str() |
| assert "x = 1" not in out |
| assert "x = 2" not in out |
| result.stdout.fnmatch_lines([" *asd*", "E*NameError*"]) |
| result = testdir.runpytest("--fulltrace") |
| out = result.stdout.str() |
| assert "x = 1" in out |
| assert "x = 2" in out |
| result.stdout.fnmatch_lines([">*asd*", "E*NameError*"]) |
| |
| def test_traceback_filter_error_during_fixture_collection(self, testdir): |
| """integration test for issue #995. |
| """ |
| testdir.makepyfile( |
| """ |
| import pytest |
| |
| def fail_me(func): |
| ns = {} |
| exec('def w(): raise ValueError("fail me")', ns) |
| return ns['w'] |
| |
| @pytest.fixture(scope='class') |
| @fail_me |
| def fail_fixture(): |
| pass |
| |
| def test_failing_fixture(fail_fixture): |
| pass |
| """ |
| ) |
| result = testdir.runpytest() |
| assert result.ret != 0 |
| out = result.stdout.str() |
| assert "INTERNALERROR>" not in out |
| result.stdout.fnmatch_lines(["*ValueError: fail me*", "* 1 error in *"]) |
| |
| def test_filter_traceback_generated_code(self): |
| """test that filter_traceback() works with the fact that |
| py.code.Code.path attribute might return an str object. |
| In this case, one of the entries on the traceback was produced by |
| dynamically generated code. |
| See: https://bitbucket.org/pytest-dev/py/issues/71 |
| This fixes #995. |
| """ |
| from _pytest.python import filter_traceback |
| |
| try: |
| ns = {} |
| exec("def foo(): raise ValueError", ns) |
| ns["foo"]() |
| except ValueError: |
| _, _, tb = sys.exc_info() |
| |
| tb = _pytest._code.Traceback(tb) |
| assert isinstance(tb[-1].path, str) |
| assert not filter_traceback(tb[-1]) |
| |
| def test_filter_traceback_path_no_longer_valid(self, testdir): |
| """test that filter_traceback() works with the fact that |
| py.code.Code.path attribute might return an str object. |
| In this case, one of the files in the traceback no longer exists. |
| This fixes #1133. |
| """ |
| from _pytest.python import filter_traceback |
| |
| testdir.syspathinsert() |
| testdir.makepyfile( |
| filter_traceback_entry_as_str=""" |
| def foo(): |
| raise ValueError |
| """ |
| ) |
| try: |
| import filter_traceback_entry_as_str |
| |
| filter_traceback_entry_as_str.foo() |
| except ValueError: |
| _, _, tb = sys.exc_info() |
| |
| testdir.tmpdir.join("filter_traceback_entry_as_str.py").remove() |
| tb = _pytest._code.Traceback(tb) |
| assert isinstance(tb[-1].path, str) |
| assert filter_traceback(tb[-1]) |
| |
| |
| class TestReportInfo(object): |
| |
| def test_itemreport_reportinfo(self, testdir, linecomp): |
| testdir.makeconftest( |
| """ |
| import pytest |
| class MyFunction(pytest.Function): |
| def reportinfo(self): |
| return "ABCDE", 42, "custom" |
| def pytest_pycollect_makeitem(collector, name, obj): |
| if name == "test_func": |
| return MyFunction(name, parent=collector) |
| """ |
| ) |
| item = testdir.getitem("def test_func(): pass") |
| item.config.pluginmanager.getplugin("runner") |
| assert item.location == ("ABCDE", 42, "custom") |
| |
| def test_func_reportinfo(self, testdir): |
| item = testdir.getitem("def test_func(): pass") |
| fspath, lineno, modpath = item.reportinfo() |
| assert fspath == item.fspath |
| assert lineno == 0 |
| assert modpath == "test_func" |
| |
| def test_class_reportinfo(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| # lineno 0 |
| class TestClass(object): |
| def test_hello(self): pass |
| """ |
| ) |
| classcol = testdir.collect_by_name(modcol, "TestClass") |
| fspath, lineno, msg = classcol.reportinfo() |
| assert fspath == modcol.fspath |
| assert lineno == 1 |
| assert msg == "TestClass" |
| |
| def test_generator_reportinfo(self, testdir): |
| modcol = testdir.getmodulecol( |
| """ |
| # lineno 0 |
| def test_gen(): |
| def check(x): |
| assert x |
| yield check, 3 |
| """ |
| ) |
| gencol = testdir.collect_by_name(modcol, "test_gen") |
| fspath, lineno, modpath = gencol.reportinfo() |
| assert fspath == modcol.fspath |
| assert lineno == 1 |
| assert modpath == "test_gen" |
| |
| genitem = gencol.collect()[0] |
| fspath, lineno, modpath = genitem.reportinfo() |
| assert fspath == modcol.fspath |
| assert lineno == 2 |
| assert modpath == "test_gen[0]" |
| """ |
| def test_func(): |
| pass |
| def test_genfunc(): |
| def check(x): |
| pass |
| yield check, 3 |
| class TestClass(object): |
| def test_method(self): |
| pass |
| """ |
| |
| def test_reportinfo_with_nasty_getattr(self, testdir): |
| # https://github.com/pytest-dev/pytest/issues/1204 |
| modcol = testdir.getmodulecol( |
| """ |
| # lineno 0 |
| class TestClass(object): |
| def __getattr__(self, name): |
| return "this is not an int" |
| |
| def test_foo(self): |
| pass |
| """ |
| ) |
| classcol = testdir.collect_by_name(modcol, "TestClass") |
| instance = classcol.collect()[0] |
| fspath, lineno, msg = instance.reportinfo() |
| |
| |
| def test_customized_python_discovery(testdir): |
| testdir.makeini( |
| """ |
| [pytest] |
| python_files=check_*.py |
| python_classes=Check |
| python_functions=check |
| """ |
| ) |
| p = testdir.makepyfile( |
| """ |
| def check_simple(): |
| pass |
| class CheckMyApp(object): |
| def check_meth(self): |
| pass |
| """ |
| ) |
| p2 = p.new(basename=p.basename.replace("test", "check")) |
| p.move(p2) |
| result = testdir.runpytest("--collect-only", "-s") |
| result.stdout.fnmatch_lines( |
| ["*check_customized*", "*check_simple*", "*CheckMyApp*", "*check_meth*"] |
| ) |
| |
| result = testdir.runpytest() |
| assert result.ret == 0 |
| result.stdout.fnmatch_lines(["*2 passed*"]) |
| |
| |
| def test_customized_python_discovery_functions(testdir): |
| testdir.makeini( |
| """ |
| [pytest] |
| python_functions=_test |
| """ |
| ) |
| testdir.makepyfile( |
| """ |
| def _test_underscore(): |
| pass |
| """ |
| ) |
| result = testdir.runpytest("--collect-only", "-s") |
| result.stdout.fnmatch_lines(["*_test_underscore*"]) |
| |
| result = testdir.runpytest() |
| assert result.ret == 0 |
| result.stdout.fnmatch_lines(["*1 passed*"]) |
| |
| |
| def test_collector_attributes(testdir): |
| testdir.makeconftest( |
| """ |
| import pytest |
| def pytest_pycollect_makeitem(collector): |
| assert collector.Function == pytest.Function |
| assert collector.Class == pytest.Class |
| assert collector.Instance == pytest.Instance |
| assert collector.Module == pytest.Module |
| """ |
| ) |
| testdir.makepyfile( |
| """ |
| def test_hello(): |
| pass |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines(["*1 passed*"]) |
| |
| |
| def test_customize_through_attributes(testdir): |
| testdir.makeconftest( |
| """ |
| import pytest |
| class MyFunction(pytest.Function): |
| pass |
| class MyInstance(pytest.Instance): |
| Function = MyFunction |
| class MyClass(pytest.Class): |
| Instance = MyInstance |
| |
| def pytest_pycollect_makeitem(collector, name, obj): |
| if name.startswith("MyTestClass"): |
| return MyClass(name, parent=collector) |
| """ |
| ) |
| testdir.makepyfile( |
| """ |
| class MyTestClass(object): |
| def test_hello(self): |
| pass |
| """ |
| ) |
| result = testdir.runpytest("--collect-only") |
| result.stdout.fnmatch_lines( |
| ["*MyClass*", "*MyInstance*", "*MyFunction*test_hello*"] |
| ) |
| |
| |
| def test_unorderable_types(testdir): |
| testdir.makepyfile( |
| """ |
| class TestJoinEmpty(object): |
| pass |
| |
| def make_test(): |
| class Test(object): |
| pass |
| Test.__name__ = "TestFoo" |
| return Test |
| TestFoo = make_test() |
| """ |
| ) |
| result = testdir.runpytest() |
| assert "TypeError" not in result.stdout.str() |
| assert result.ret == EXIT_NOTESTSCOLLECTED |
| |
| |
| def test_collect_functools_partial(testdir): |
| """ |
| Test that collection of functools.partial object works, and arguments |
| to the wrapped functions are dealt correctly (see #811). |
| """ |
| testdir.makepyfile( |
| """ |
| import functools |
| import pytest |
| |
| @pytest.fixture |
| def fix1(): |
| return 'fix1' |
| |
| @pytest.fixture |
| def fix2(): |
| return 'fix2' |
| |
| def check1(i, fix1): |
| assert i == 2 |
| assert fix1 == 'fix1' |
| |
| def check2(fix1, i): |
| assert i == 2 |
| assert fix1 == 'fix1' |
| |
| def check3(fix1, i, fix2): |
| assert i == 2 |
| assert fix1 == 'fix1' |
| assert fix2 == 'fix2' |
| |
| test_ok_1 = functools.partial(check1, i=2) |
| test_ok_2 = functools.partial(check1, i=2, fix1='fix1') |
| test_ok_3 = functools.partial(check1, 2) |
| test_ok_4 = functools.partial(check2, i=2) |
| test_ok_5 = functools.partial(check3, i=2) |
| test_ok_6 = functools.partial(check3, i=2, fix1='fix1') |
| |
| test_fail_1 = functools.partial(check2, 2) |
| test_fail_2 = functools.partial(check3, 2) |
| """ |
| ) |
| result = testdir.inline_run() |
| result.assertoutcome(passed=6, failed=2) |
| |
| |
| def test_dont_collect_non_function_callable(testdir): |
| """Test for issue https://github.com/pytest-dev/pytest/issues/331 |
| |
| In this case an INTERNALERROR occurred trying to report the failure of |
| a test like this one because py test failed to get the source lines. |
| """ |
| testdir.makepyfile( |
| """ |
| class Oh(object): |
| def __call__(self): |
| pass |
| |
| test_a = Oh() |
| |
| def test_real(): |
| pass |
| """ |
| ) |
| result = testdir.runpytest("-rw") |
| result.stdout.fnmatch_lines( |
| [ |
| "*collected 1 item*", |
| "*cannot collect 'test_a' because it is not a function*", |
| "*1 passed, 1 warnings in *", |
| ] |
| ) |
| |
| |
| def test_class_injection_does_not_break_collection(testdir): |
| """Tests whether injection during collection time will terminate testing. |
| |
| In this case the error should not occur if the TestClass itself |
| is modified during collection time, and the original method list |
| is still used for collection. |
| """ |
| testdir.makeconftest( |
| """ |
| from test_inject import TestClass |
| def pytest_generate_tests(metafunc): |
| TestClass.changed_var = {} |
| """ |
| ) |
| testdir.makepyfile( |
| test_inject=''' |
| class TestClass(object): |
| def test_injection(self): |
| """Test being parametrized.""" |
| pass |
| ''' |
| ) |
| result = testdir.runpytest() |
| assert "RuntimeError: dictionary changed size during iteration" not in result.stdout.str() |
| result.stdout.fnmatch_lines(["*1 passed*"]) |
| |
| |
| def test_syntax_error_with_non_ascii_chars(testdir): |
| """Fix decoding issue while formatting SyntaxErrors during collection (#578) |
| """ |
| testdir.makepyfile( |
| u""" |
| # -*- coding: UTF-8 -*- |
| |
| ☃ |
| """ |
| ) |
| result = testdir.runpytest() |
| result.stdout.fnmatch_lines(["*ERROR collecting*", "*SyntaxError*", "*1 error in*"]) |
| |
| |
| def test_skip_duplicates_by_default(testdir): |
| """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609) |
| |
| Ignore duplicate directories. |
| """ |
| a = testdir.mkdir("a") |
| fh = a.join("test_a.py") |
| fh.write( |
| _pytest._code.Source( |
| """ |
| import pytest |
| def test_real(): |
| pass |
| """ |
| ) |
| ) |
| result = testdir.runpytest(a.strpath, a.strpath) |
| result.stdout.fnmatch_lines(["*collected 1 item*"]) |
| |
| |
| def test_keep_duplicates(testdir): |
| """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609) |
| |
| Use --keep-duplicates to collect tests from duplicate directories. |
| """ |
| a = testdir.mkdir("a") |
| fh = a.join("test_a.py") |
| fh.write( |
| _pytest._code.Source( |
| """ |
| import pytest |
| def test_real(): |
| pass |
| """ |
| ) |
| ) |
| result = testdir.runpytest("--keep-duplicates", a.strpath, a.strpath) |
| result.stdout.fnmatch_lines(["*collected 2 item*"]) |