test_ok1/testing/test_mark.py

1126 lines
31 KiB
Python
Raw Normal View History

from __future__ import absolute_import, division, print_function
import os
import sys
import pytest
from _pytest.mark import (
2018-05-23 22:48:46 +08:00
MarkGenerator as Mark,
ParameterSet,
transfer_markers,
EMPTY_PARAMETERSET_OPTION,
)
2018-05-23 22:48:46 +08:00
ignore_markinfo = pytest.mark.filterwarnings(
"ignore:MarkInfo objects:_pytest.deprecated.RemovedInPytest4Warning"
)
class TestMark(object):
2018-05-23 22:48:46 +08:00
def test_markinfo_repr(self):
from _pytest.mark import MarkInfo, Mark
2018-05-23 22:48:46 +08:00
m = MarkInfo.for_mark(Mark("hello", (1, 2), {}))
repr(m)
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize("attr", ["mark", "param"])
@pytest.mark.parametrize("modulename", ["py.test", "pytest"])
def test_pytest_exists_in_namespace_all(self, attr, modulename):
module = sys.modules[modulename]
assert attr in module.__all__
def test_pytest_mark_notcallable(self):
mark = Mark()
pytest.raises((AttributeError, TypeError), mark)
2017-07-21 13:26:56 +08:00
def test_mark_with_param(self):
2018-05-23 22:48:46 +08:00
2017-07-21 13:26:56 +08:00
def some_function(abc):
pass
class SomeClass(object):
pass
assert pytest.mark.fun(some_function) is some_function
assert pytest.mark.fun.with_args(some_function) is not some_function
assert pytest.mark.fun(SomeClass) is SomeClass
assert pytest.mark.fun.with_args(SomeClass) is not SomeClass
def test_pytest_mark_name_starts_with_underscore(self):
mark = Mark()
2018-05-23 22:48:46 +08:00
pytest.raises(AttributeError, getattr, mark, "_some_name")
def test_pytest_mark_bare(self):
mark = Mark()
def f():
pass
mark.hello(f)
assert f.hello
@ignore_markinfo
def test_pytest_mark_keywords(self):
mark = Mark()
def f():
pass
mark.world(x=3, y=4)(f)
assert f.world
2018-05-23 22:48:46 +08:00
assert f.world.kwargs["x"] == 3
assert f.world.kwargs["y"] == 4
@ignore_markinfo
def test_apply_multiple_and_merge(self):
mark = Mark()
def f():
pass
mark.world
mark.world(x=3)(f)
2018-05-23 22:48:46 +08:00
assert f.world.kwargs["x"] == 3
mark.world(y=4)(f)
2018-05-23 22:48:46 +08:00
assert f.world.kwargs["x"] == 3
assert f.world.kwargs["y"] == 4
mark.world(y=1)(f)
2018-05-23 22:48:46 +08:00
assert f.world.kwargs["y"] == 1
assert len(f.world.args) == 0
@ignore_markinfo
def test_pytest_mark_positional(self):
mark = Mark()
def f():
pass
mark.world("hello")(f)
assert f.world.args[0] == "hello"
mark.world("world")(f)
@ignore_markinfo
fix handling MarkDecorators called with a single positional plus keyword args When a MarkDecorator instance is called it does the following: 1. If called with a single class as its only positional argument and no additional keyword arguments, it attaches itself to the class so it gets applied automatically to all test cases found in that class. 2. If called with a single function as its only positional argument and no additional keyword arguments, it attaches a MarkInfo object to the function, containing all the arguments already stored internally in the MarkDecorator. 3. When called in any other case, it performs a 'fake construction' call, i.e. it returns a new MarkDecorator instance with the original MarkDecorator's content updated with the arguments passed to this call. When Python applies a function decorator it always passes the target class/ function to the decorator as its positional argument with no additional positional or keyword arguments. However, when MarkDecorator was deciding whether it was being called to decorate a target function/class (cases 1. & 2. as documented above) or to return an updated MarkDecorator (case 3. as documented above), it only checked that it received a single callable positional argument and did not take into consideration whether additional keyword arguments were being passed in as well. With this change, it is now possible to create a pytest mark storing a function/ class parameter passed as its only positional argument and accompanied by one or more additional keyword arguments. Before, it was only possible to do so if the function/class parameter argument was accompanied by at least one other positional argument. Added a related unit test. Updated MarkDecorator doc-string.
2014-01-20 08:27:33 +08:00
def test_pytest_mark_positional_func_and_keyword(self):
mark = Mark()
fix handling MarkDecorators called with a single positional plus keyword args When a MarkDecorator instance is called it does the following: 1. If called with a single class as its only positional argument and no additional keyword arguments, it attaches itself to the class so it gets applied automatically to all test cases found in that class. 2. If called with a single function as its only positional argument and no additional keyword arguments, it attaches a MarkInfo object to the function, containing all the arguments already stored internally in the MarkDecorator. 3. When called in any other case, it performs a 'fake construction' call, i.e. it returns a new MarkDecorator instance with the original MarkDecorator's content updated with the arguments passed to this call. When Python applies a function decorator it always passes the target class/ function to the decorator as its positional argument with no additional positional or keyword arguments. However, when MarkDecorator was deciding whether it was being called to decorate a target function/class (cases 1. & 2. as documented above) or to return an updated MarkDecorator (case 3. as documented above), it only checked that it received a single callable positional argument and did not take into consideration whether additional keyword arguments were being passed in as well. With this change, it is now possible to create a pytest mark storing a function/ class parameter passed as its only positional argument and accompanied by one or more additional keyword arguments. Before, it was only possible to do so if the function/class parameter argument was accompanied by at least one other positional argument. Added a related unit test. Updated MarkDecorator doc-string.
2014-01-20 08:27:33 +08:00
def f():
raise Exception
fix handling MarkDecorators called with a single positional plus keyword args When a MarkDecorator instance is called it does the following: 1. If called with a single class as its only positional argument and no additional keyword arguments, it attaches itself to the class so it gets applied automatically to all test cases found in that class. 2. If called with a single function as its only positional argument and no additional keyword arguments, it attaches a MarkInfo object to the function, containing all the arguments already stored internally in the MarkDecorator. 3. When called in any other case, it performs a 'fake construction' call, i.e. it returns a new MarkDecorator instance with the original MarkDecorator's content updated with the arguments passed to this call. When Python applies a function decorator it always passes the target class/ function to the decorator as its positional argument with no additional positional or keyword arguments. However, when MarkDecorator was deciding whether it was being called to decorate a target function/class (cases 1. & 2. as documented above) or to return an updated MarkDecorator (case 3. as documented above), it only checked that it received a single callable positional argument and did not take into consideration whether additional keyword arguments were being passed in as well. With this change, it is now possible to create a pytest mark storing a function/ class parameter passed as its only positional argument and accompanied by one or more additional keyword arguments. Before, it was only possible to do so if the function/class parameter argument was accompanied by at least one other positional argument. Added a related unit test. Updated MarkDecorator doc-string.
2014-01-20 08:27:33 +08:00
m = mark.world(f, omega="hello")
fix handling MarkDecorators called with a single positional plus keyword args When a MarkDecorator instance is called it does the following: 1. If called with a single class as its only positional argument and no additional keyword arguments, it attaches itself to the class so it gets applied automatically to all test cases found in that class. 2. If called with a single function as its only positional argument and no additional keyword arguments, it attaches a MarkInfo object to the function, containing all the arguments already stored internally in the MarkDecorator. 3. When called in any other case, it performs a 'fake construction' call, i.e. it returns a new MarkDecorator instance with the original MarkDecorator's content updated with the arguments passed to this call. When Python applies a function decorator it always passes the target class/ function to the decorator as its positional argument with no additional positional or keyword arguments. However, when MarkDecorator was deciding whether it was being called to decorate a target function/class (cases 1. & 2. as documented above) or to return an updated MarkDecorator (case 3. as documented above), it only checked that it received a single callable positional argument and did not take into consideration whether additional keyword arguments were being passed in as well. With this change, it is now possible to create a pytest mark storing a function/ class parameter passed as its only positional argument and accompanied by one or more additional keyword arguments. Before, it was only possible to do so if the function/class parameter argument was accompanied by at least one other positional argument. Added a related unit test. Updated MarkDecorator doc-string.
2014-01-20 08:27:33 +08:00
def g():
pass
fix handling MarkDecorators called with a single positional plus keyword args When a MarkDecorator instance is called it does the following: 1. If called with a single class as its only positional argument and no additional keyword arguments, it attaches itself to the class so it gets applied automatically to all test cases found in that class. 2. If called with a single function as its only positional argument and no additional keyword arguments, it attaches a MarkInfo object to the function, containing all the arguments already stored internally in the MarkDecorator. 3. When called in any other case, it performs a 'fake construction' call, i.e. it returns a new MarkDecorator instance with the original MarkDecorator's content updated with the arguments passed to this call. When Python applies a function decorator it always passes the target class/ function to the decorator as its positional argument with no additional positional or keyword arguments. However, when MarkDecorator was deciding whether it was being called to decorate a target function/class (cases 1. & 2. as documented above) or to return an updated MarkDecorator (case 3. as documented above), it only checked that it received a single callable positional argument and did not take into consideration whether additional keyword arguments were being passed in as well. With this change, it is now possible to create a pytest mark storing a function/ class parameter passed as its only positional argument and accompanied by one or more additional keyword arguments. Before, it was only possible to do so if the function/class parameter argument was accompanied by at least one other positional argument. Added a related unit test. Updated MarkDecorator doc-string.
2014-01-20 08:27:33 +08:00
assert m(g) == g
assert g.world.args[0] is f
assert g.world.kwargs["omega"] == "hello"
@ignore_markinfo
def test_pytest_mark_reuse(self):
mark = Mark()
def f():
pass
w = mark.some
w("hello", reason="123")(f)
assert f.some.args[0] == "hello"
2018-05-23 22:48:46 +08:00
assert f.some.kwargs["reason"] == "123"
def g():
pass
w("world", reason2="456")(g)
assert g.some.args[0] == "world"
2018-05-23 22:48:46 +08:00
assert "reason" not in g.some.kwargs
assert g.some.kwargs["reason2"] == "456"
def test_marked_class_run_twice(testdir, request):
"""Test fails file is run twice that contains marked class.
See issue#683.
"""
2018-05-23 22:48:46 +08:00
py_file = testdir.makepyfile(
"""
import pytest
@pytest.mark.parametrize('abc', [1, 2, 3])
class Test1(object):
def test_1(self, abc):
assert abc in [1, 2, 3]
2018-05-23 22:48:46 +08:00
"""
)
file_name = os.path.basename(py_file.strpath)
rec = testdir.inline_run(file_name, file_name)
rec.assertoutcome(passed=6)
def test_ini_markers(testdir):
2018-05-23 22:48:46 +08:00
testdir.makeini(
"""
[pytest]
markers =
a1: this is a webtest marker
a2: this is a smoke marker
2018-05-23 22:48:46 +08:00
"""
)
testdir.makepyfile(
"""
def test_markers(pytestconfig):
markers = pytestconfig.getini("markers")
print (markers)
assert len(markers) >= 2
assert markers[0].startswith("a1:")
assert markers[1].startswith("a2:")
2018-05-23 22:48:46 +08:00
"""
)
rec = testdir.inline_run()
rec.assertoutcome(passed=1)
def test_markers_option(testdir):
2018-05-23 22:48:46 +08:00
testdir.makeini(
"""
[pytest]
markers =
a1: this is a webtest marker
a1some: another marker
2017-11-22 20:47:15 +08:00
nodescription
2018-05-23 22:48:46 +08:00
"""
)
result = testdir.runpytest("--markers")
result.stdout.fnmatch_lines(
["*a1*this is a webtest*", "*a1some*another marker", "*nodescription*"]
)
def test_ini_markers_whitespace(testdir):
2018-05-23 22:48:46 +08:00
testdir.makeini(
"""
[pytest]
markers =
a1 : this is a whitespace marker
2018-05-23 22:48:46 +08:00
"""
)
testdir.makepyfile(
"""
import pytest
@pytest.mark.a1
def test_markers():
assert True
2018-05-23 22:48:46 +08:00
"""
)
rec = testdir.inline_run("--strict", "-m", "a1")
rec.assertoutcome(passed=1)
2017-11-22 20:47:15 +08:00
def test_marker_without_description(testdir):
2018-05-23 22:48:46 +08:00
testdir.makefile(
".cfg",
setup="""
2017-11-22 20:47:15 +08:00
[tool:pytest]
markers=slow
2018-05-23 22:48:46 +08:00
""",
)
testdir.makeconftest(
"""
2017-11-22 20:47:15 +08:00
import pytest
pytest.mark.xfail('FAIL')
2018-05-23 22:48:46 +08:00
"""
)
2017-11-22 20:47:15 +08:00
ftdir = testdir.mkdir("ft1_dummy")
testdir.tmpdir.join("conftest.py").move(ftdir.join("conftest.py"))
rec = testdir.runpytest_subprocess("--strict")
rec.assert_outcomes()
def test_markers_option_with_plugin_in_current_dir(testdir):
testdir.makeconftest('pytest_plugins = "flip_flop"')
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
flip_flop="""\
def pytest_configure(config):
config.addinivalue_line("markers", "flip:flop")
def pytest_generate_tests(metafunc):
try:
mark = metafunc.function.flipper
except AttributeError:
return
2018-05-23 22:48:46 +08:00
metafunc.parametrize("x", (10, 20))"""
)
testdir.makepyfile(
"""\
import pytest
@pytest.mark.flipper
def test_example(x):
2018-05-23 22:48:46 +08:00
assert x"""
)
result = testdir.runpytest("--markers")
result.stdout.fnmatch_lines(["*flip*flop*"])
def test_mark_on_pseudo_function(testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
import pytest
@pytest.mark.r(lambda x: 0/0)
def test_hello():
pass
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
def test_strict_prohibits_unregistered_markers(testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
import pytest
@pytest.mark.unregisteredmark
def test_hello():
pass
2018-05-23 22:48:46 +08:00
"""
)
result = testdir.runpytest("--strict")
assert result.ret != 0
2018-05-23 22:48:46 +08:00
result.stdout.fnmatch_lines(["*unregisteredmark*not*registered*"])
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize(
"spec",
[
("xyz", ("test_one",)),
("xyz and xyz2", ()),
("xyz2", ("test_two",)),
("xyz or xyz2", ("test_one", "test_two")),
],
)
def test_mark_option(spec, testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
import pytest
@pytest.mark.xyz
def test_one():
pass
@pytest.mark.xyz2
def test_two():
pass
2018-05-23 22:48:46 +08:00
"""
)
opt, passed_result = spec
rec = testdir.inline_run("-m", opt)
passed, skipped, fail = rec.listoutcomes()
passed = [x.nodeid.split("::")[-1] for x in passed]
assert len(passed) == len(passed_result)
assert list(passed) == list(passed_result)
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize(
"spec", [("interface", ("test_interface",)), ("not interface", ("test_nointer",))]
)
def test_mark_option_custom(spec, testdir):
2018-05-23 22:48:46 +08:00
testdir.makeconftest(
"""
import pytest
def pytest_collection_modifyitems(items):
for item in items:
if "interface" in item.nodeid:
item.add_marker(pytest.mark.interface)
2018-05-23 22:48:46 +08:00
"""
)
testdir.makepyfile(
"""
def test_interface():
pass
def test_nointer():
pass
2018-05-23 22:48:46 +08:00
"""
)
opt, passed_result = spec
rec = testdir.inline_run("-m", opt)
passed, skipped, fail = rec.listoutcomes()
passed = [x.nodeid.split("::")[-1] for x in passed]
assert len(passed) == len(passed_result)
assert list(passed) == list(passed_result)
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize(
"spec",
[
("interface", ("test_interface",)),
("not interface", ("test_nointer", "test_pass")),
("pass", ("test_pass",)),
("not pass", ("test_interface", "test_nointer")),
],
)
def test_keyword_option_custom(spec, testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
def test_interface():
pass
def test_nointer():
pass
def test_pass():
pass
2018-05-23 22:48:46 +08:00
"""
)
opt, passed_result = spec
rec = testdir.inline_run("-k", opt)
passed, skipped, fail = rec.listoutcomes()
passed = [x.nodeid.split("::")[-1] for x in passed]
assert len(passed) == len(passed_result)
assert list(passed) == list(passed_result)
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize(
"spec",
[
("None", ("test_func[None]",)),
("1.3", ("test_func[1.3]",)),
("2-3", ("test_func[2-3]",)),
],
)
def test_keyword_option_parametrize(spec, testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
import pytest
@pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
def test_func(arg):
pass
2018-05-23 22:48:46 +08:00
"""
)
opt, passed_result = spec
rec = testdir.inline_run("-k", opt)
passed, skipped, fail = rec.listoutcomes()
passed = [x.nodeid.split("::")[-1] for x in passed]
assert len(passed) == len(passed_result)
assert list(passed) == list(passed_result)
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize(
"spec",
[
(
"foo or import",
"ERROR: Python keyword 'import' not accepted in expressions passed to '-k'",
),
("foo or", "ERROR: Wrong expression passed to '-k': foo or"),
],
)
def test_keyword_option_wrong_arguments(spec, testdir, capsys):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
def test_func(arg):
pass
2018-05-23 22:48:46 +08:00
"""
)
opt, expected_result = spec
testdir.inline_run("-k", opt)
2018-01-17 04:35:57 +08:00
out = capsys.readouterr().err
assert expected_result in out
def test_parametrized_collected_from_command_line(testdir):
"""Parametrized test not collected if test named specified
in command line issue#649.
"""
2018-05-23 22:48:46 +08:00
py_file = testdir.makepyfile(
"""
import pytest
@pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
def test_func(arg):
pass
2018-05-23 22:48:46 +08:00
"""
)
file_name = os.path.basename(py_file.strpath)
rec = testdir.inline_run(file_name + "::" + "test_func")
rec.assertoutcome(passed=3)
def test_parametrized_collect_with_wrong_args(testdir):
"""Test collect parametrized func with wrong number of args."""
2018-05-23 22:48:46 +08:00
py_file = testdir.makepyfile(
"""
import pytest
@pytest.mark.parametrize('foo, bar', [(1, 2, 3)])
def test_func(foo, bar):
pass
2018-05-23 22:48:46 +08:00
"""
)
result = testdir.runpytest(py_file)
2018-05-23 22:48:46 +08:00
result.stdout.fnmatch_lines(
[
'E ValueError: In "parametrize" the number of values ((1, 2, 3)) '
"must be equal to the number of names (['foo', 'bar'])"
]
)
2017-10-09 00:02:54 +08:00
def test_parametrized_with_kwargs(testdir):
"""Test collect parametrized func with wrong number of args."""
2018-05-23 22:48:46 +08:00
py_file = testdir.makepyfile(
"""
2017-10-09 00:02:54 +08:00
import pytest
@pytest.fixture(params=[1,2])
def a(request):
return request.param
@pytest.mark.parametrize(argnames='b', argvalues=[1, 2])
def test_func(a, b):
pass
2018-05-23 22:48:46 +08:00
"""
)
2017-10-09 00:02:54 +08:00
result = testdir.runpytest(py_file)
2018-05-23 22:48:46 +08:00
assert result.ret == 0
2017-10-09 00:02:54 +08:00
class TestFunctional(object):
def test_mark_per_function(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
import pytest
@pytest.mark.hello
def test_hello():
assert hasattr(test_hello, 'hello')
2018-05-23 22:48:46 +08:00
"""
)
result = testdir.runpytest(p)
result.stdout.fnmatch_lines(["*1 passed*"])
def test_mark_per_module(self, testdir):
2018-05-23 22:48:46 +08:00
item = testdir.getitem(
"""
import pytest
pytestmark = pytest.mark.hello
def test_func():
pass
2018-05-23 22:48:46 +08:00
"""
)
keywords = item.keywords
2018-05-23 22:48:46 +08:00
assert "hello" in keywords
def test_marklist_per_class(self, testdir):
2018-05-23 22:48:46 +08:00
item = testdir.getitem(
"""
import pytest
class TestClass(object):
pytestmark = [pytest.mark.hello, pytest.mark.world]
def test_func(self):
assert TestClass.test_func.hello
assert TestClass.test_func.world
2018-05-23 22:48:46 +08:00
"""
)
keywords = item.keywords
2018-05-23 22:48:46 +08:00
assert "hello" in keywords
def test_marklist_per_module(self, testdir):
2018-05-23 22:48:46 +08:00
item = testdir.getitem(
"""
import pytest
pytestmark = [pytest.mark.hello, pytest.mark.world]
class TestClass(object):
def test_func(self):
assert TestClass.test_func.hello
assert TestClass.test_func.world
2018-05-23 22:48:46 +08:00
"""
)
keywords = item.keywords
2018-05-23 22:48:46 +08:00
assert "hello" in keywords
assert "world" in keywords
def test_mark_per_class_decorator(self, testdir):
2018-05-23 22:48:46 +08:00
item = testdir.getitem(
"""
import pytest
@pytest.mark.hello
class TestClass(object):
def test_func(self):
assert TestClass.test_func.hello
2018-05-23 22:48:46 +08:00
"""
)
keywords = item.keywords
2018-05-23 22:48:46 +08:00
assert "hello" in keywords
def test_mark_per_class_decorator_plus_existing_dec(self, testdir):
2018-05-23 22:48:46 +08:00
item = testdir.getitem(
"""
import pytest
@pytest.mark.hello
class TestClass(object):
pytestmark = pytest.mark.world
def test_func(self):
assert TestClass.test_func.hello
assert TestClass.test_func.world
2018-05-23 22:48:46 +08:00
"""
)
keywords = item.keywords
2018-05-23 22:48:46 +08:00
assert "hello" in keywords
assert "world" in keywords
@ignore_markinfo
def test_merging_markers(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
import pytest
pytestmark = pytest.mark.hello("pos1", x=1, y=2)
class TestClass(object):
# classlevel overrides module level
pytestmark = pytest.mark.hello(x=3)
@pytest.mark.hello("pos0", z=4)
def test_func(self):
pass
2018-05-23 22:48:46 +08:00
"""
)
items, rec = testdir.inline_genitems(p)
item, = items
keywords = item.keywords
2018-05-23 22:48:46 +08:00
marker = keywords["hello"]
assert marker.args == ("pos0", "pos1")
2018-05-23 22:48:46 +08:00
assert marker.kwargs == {"x": 1, "y": 2, "z": 4}
# test the new __iter__ interface
values = list(marker)
assert len(values) == 3
assert values[0].args == ("pos0",)
assert values[1].args == ()
2018-05-23 22:48:46 +08:00
assert values[2].args == ("pos1",)
2012-11-06 04:52:12 +08:00
def test_merging_markers_deep(self, testdir):
# issue 199 - propagate markers into nested classes
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
2012-11-06 04:52:12 +08:00
import pytest
class TestA(object):
2012-11-06 04:52:12 +08:00
pytestmark = pytest.mark.a
def test_b(self):
assert True
class TestC(object):
2012-11-06 04:52:12 +08:00
# this one didnt get marked
def test_d(self):
assert True
2018-05-23 22:48:46 +08:00
"""
)
2012-11-06 04:52:12 +08:00
items, rec = testdir.inline_genitems(p)
for item in items:
print(item, item.keywords)
2018-05-23 22:48:46 +08:00
assert [x for x in item.iter_markers() if x.name == "a"]
2012-11-06 04:52:12 +08:00
def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
import pytest
@pytest.mark.a
class Base(object): pass
@pytest.mark.b
class Test1(Base):
def test_foo(self): pass
class Test2(Base):
def test_bar(self): pass
2018-05-23 22:48:46 +08:00
"""
)
items, rec = testdir.inline_genitems(p)
2018-05-23 22:48:46 +08:00
self.assert_markers(items, test_foo=("a", "b"), test_bar=("a",))
2016-06-25 17:10:23 +08:00
@pytest.mark.issue568
def test_mark_should_not_pass_to_siebling_class(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
2016-06-25 17:10:23 +08:00
import pytest
class TestBase(object):
2016-06-25 17:10:23 +08:00
def test_foo(self):
pass
@pytest.mark.b
class TestSub(TestBase):
pass
class TestOtherSub(TestBase):
pass
2018-05-23 22:48:46 +08:00
"""
)
2016-06-25 17:10:23 +08:00
items, rec = testdir.inline_genitems(p)
base_item, sub_item, sub_item_other = items
print(items, [x.nodeid for x in items])
# legacy api smears
2018-05-23 22:48:46 +08:00
assert hasattr(base_item.obj, "b")
assert hasattr(sub_item_other.obj, "b")
assert hasattr(sub_item.obj, "b")
# new api seregates
2018-05-23 22:48:46 +08:00
assert not list(base_item.iter_markers(name="b"))
assert not list(sub_item_other.iter_markers(name="b"))
assert list(sub_item.iter_markers(name="b"))
2016-06-25 17:10:23 +08:00
def test_mark_decorator_baseclasses_merged(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
import pytest
@pytest.mark.a
class Base(object): pass
@pytest.mark.b
class Base2(Base): pass
@pytest.mark.c
class Test1(Base2):
def test_foo(self): pass
class Test2(Base2):
@pytest.mark.d
def test_bar(self): pass
2018-05-23 22:48:46 +08:00
"""
)
items, rec = testdir.inline_genitems(p)
2018-05-23 22:48:46 +08:00
self.assert_markers(items, test_foo=("a", "b", "c"), test_bar=("a", "b", "d"))
def test_mark_closest(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
import pytest
@pytest.mark.c(location="class")
class Test:
@pytest.mark.c(location="function")
def test_has_own():
pass
def test_has_inherited():
pass
2018-05-23 22:48:46 +08:00
"""
)
items, rec = testdir.inline_genitems(p)
has_own, has_inherited = items
2018-05-23 22:48:46 +08:00
assert has_own.get_closest_marker("c").kwargs == {"location": "function"}
assert has_inherited.get_closest_marker("c").kwargs == {"location": "class"}
assert has_own.get_closest_marker("missing") is None
def test_mark_with_wrong_marker(self, testdir):
2018-05-23 22:48:46 +08:00
reprec = testdir.inline_runsource(
"""
import pytest
class pytestmark(object):
pass
def test_func():
pass
2018-05-23 22:48:46 +08:00
"""
)
values = reprec.getfailedcollections()
assert len(values) == 1
assert "TypeError" in str(values[0].longrepr)
def test_mark_dynamically_in_funcarg(self, testdir):
2018-05-23 22:48:46 +08:00
testdir.makeconftest(
"""
import pytest
@pytest.fixture
def arg(request):
request.applymarker(pytest.mark.hello)
def pytest_terminal_summary(terminalreporter):
values = terminalreporter.stats['passed']
terminalreporter._tw.line("keyword: %s" % values[0].keywords)
2018-05-23 22:48:46 +08:00
"""
)
testdir.makepyfile(
"""
def test_func(arg):
pass
2018-05-23 22:48:46 +08:00
"""
)
result = testdir.runpytest()
2018-05-23 22:48:46 +08:00
result.stdout.fnmatch_lines(["keyword: *hello*"])
@ignore_markinfo
def test_merging_markers_two_functions(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
import pytest
@pytest.mark.hello("pos1", z=4)
@pytest.mark.hello("pos0", z=3)
def test_func():
pass
2018-05-23 22:48:46 +08:00
"""
)
items, rec = testdir.inline_genitems(p)
item, = items
keywords = item.keywords
2018-05-23 22:48:46 +08:00
marker = keywords["hello"]
values = list(marker)
assert len(values) == 2
assert values[0].args == ("pos0",)
assert values[1].args == ("pos1",)
def test_no_marker_match_on_unmarked_names(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
import pytest
@pytest.mark.shouldmatch
def test_marked():
assert 1
def test_unmarked():
assert 1
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run("-m", "test_unmarked", p)
passed, skipped, failed = reprec.listoutcomes()
assert len(passed) + len(skipped) + len(failed) == 0
dlist = reprec.getcalls("pytest_deselected")
deselected_tests = dlist[0].items
assert len(deselected_tests) == 2
def test_keywords_at_node_level(self, testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
import pytest
@pytest.fixture(scope="session", autouse=True)
def some(request):
request.keywords["hello"] = 42
assert "world" not in request.keywords
@pytest.fixture(scope="function", autouse=True)
def funcsetup(request):
assert "world" in request.keywords
assert "hello" in request.keywords
@pytest.mark.world
def test_function():
pass
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
@ignore_markinfo
def test_keyword_added_for_session(self, testdir):
2018-05-23 22:48:46 +08:00
testdir.makeconftest(
"""
import pytest
def pytest_collection_modifyitems(session):
session.add_marker("mark1")
session.add_marker(pytest.mark.mark2)
session.add_marker(pytest.mark.mark3)
pytest.raises(ValueError, lambda:
session.add_marker(10))
2018-05-23 22:48:46 +08:00
"""
)
testdir.makepyfile(
"""
def test_some(request):
assert "mark1" in request.keywords
assert "mark2" in request.keywords
assert "mark3" in request.keywords
assert 10 not in request.keywords
marker = request.node.get_marker("mark1")
assert marker.name == "mark1"
assert marker.args == ()
assert marker.kwargs == {}
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run("-m", "mark1")
reprec.assertoutcome(passed=1)
def assert_markers(self, items, **expected):
"""assert that given items have expected marker names applied to them.
expected should be a dict of (item name -> seq of expected marker names)
.. note:: this could be moved to ``testdir`` if proven to be useful
to other modules.
"""
from _pytest.mark import MarkInfo
2018-05-23 22:48:46 +08:00
2018-05-18 05:31:16 +08:00
items = {x.name: x for x in items}
for name, expected_markers in expected.items():
markers = items[name].keywords._markers
2018-05-18 16:48:44 +08:00
marker_names = {
2018-05-23 22:48:46 +08:00
name for (name, v) in markers.items() if isinstance(v, MarkInfo)
2018-05-18 16:48:44 +08:00
}
assert marker_names == set(expected_markers)
2016-04-28 22:11:30 +08:00
@pytest.mark.issue1540
@pytest.mark.filterwarnings("ignore")
2016-04-28 22:11:30 +08:00
def test_mark_from_parameters(self, testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
2016-04-28 22:11:30 +08:00
import pytest
pytestmark = pytest.mark.skipif(True, reason='skip all')
# skipifs inside fixture params
params = [pytest.mark.skipif(False, reason='dont skip')('parameter')]
@pytest.fixture(params=params)
def parameter(request):
return request.param
def test_1(parameter):
assert True
2018-05-23 22:48:46 +08:00
"""
)
2016-04-28 22:11:30 +08:00
reprec = testdir.inline_run()
reprec.assertoutcome(skipped=1)
class TestKeywordSelection(object):
def test_select_simple(self, testdir):
2018-05-23 22:48:46 +08:00
file_test = testdir.makepyfile(
"""
def test_one():
assert 0
class TestClass(object):
def test_method_one(self):
assert 42 == 43
2018-05-23 22:48:46 +08:00
"""
)
def check(keyword, name):
reprec = testdir.inline_run("-s", "-k", keyword, file_test)
passed, skipped, failed = reprec.listoutcomes()
assert len(failed) == 1
assert failed[0].nodeid.split("::")[-1] == name
2018-05-23 22:48:46 +08:00
assert len(reprec.getcalls("pytest_deselected")) == 1
for keyword in ["test_one", "est_on"]:
check(keyword, "test_one")
check("TestClass and test", "test_method_one")
@pytest.mark.parametrize(
"keyword",
[
"xxx",
"xxx and test_2",
"TestClass",
"xxx and not test_1",
"TestClass and test_2",
"xxx and TestClass and test_2",
],
)
def test_select_extra_keywords(self, testdir, keyword):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
test_select="""
def test_1():
pass
class TestClass(object):
def test_2(self):
pass
2018-05-23 22:48:46 +08:00
"""
)
testdir.makepyfile(
conftest="""
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem(name):
outcome = yield
if name == "TestClass":
item = outcome.get_result()
item.extra_keyword_matches.add("xxx")
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run(p.dirpath(), "-s", "-k", keyword)
print("keyword", repr(keyword))
passed, skipped, failed = reprec.listoutcomes()
assert len(passed) == 1
assert passed[0].nodeid.endswith("test_2")
dlist = reprec.getcalls("pytest_deselected")
assert len(dlist) == 1
2018-05-23 22:48:46 +08:00
assert dlist[0].items[0].name == "test_1"
def test_select_starton(self, testdir):
2018-05-23 22:48:46 +08:00
threepass = testdir.makepyfile(
test_threepass="""
def test_one(): assert 1
def test_two(): assert 1
def test_three(): assert 1
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run("-k", "test_two:", threepass)
passed, skipped, failed = reprec.listoutcomes()
assert len(passed) == 2
assert not failed
dlist = reprec.getcalls("pytest_deselected")
assert len(dlist) == 1
item = dlist[0].items[0]
assert item.name == "test_one"
def test_keyword_extra(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
def test_one():
assert 0
test_one.mykeyword = True
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run("-k", "mykeyword", p)
passed, skipped, failed = reprec.countoutcomes()
assert failed == 1
@pytest.mark.xfail
def test_keyword_extra_dash(self, testdir):
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
def test_one():
assert 0
test_one.mykeyword = True
2018-05-23 22:48:46 +08:00
"""
)
# with argparse the argument to an option cannot
# start with '-'
reprec = testdir.inline_run("-k", "-mykeyword", p)
passed, skipped, failed = reprec.countoutcomes()
assert passed + skipped + failed == 0
def test_no_magic_values(self, testdir):
"""Make sure the tests do not match on magic values,
no double underscored values, like '__dict__',
and no instance values, like '()'.
"""
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
def test_one(): assert 1
2018-05-23 22:48:46 +08:00
"""
)
def assert_test_is_not_selected(keyword):
reprec = testdir.inline_run("-k", keyword, p)
passed, skipped, failed = reprec.countoutcomes()
dlist = reprec.getcalls("pytest_deselected")
assert passed + skipped + failed == 0
deselected_tests = dlist[0].items
assert len(deselected_tests) == 1
assert_test_is_not_selected("__")
assert_test_is_not_selected("()")
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize(
"argval, expected",
[
(
pytest.mark.skip()((1, 2)),
ParameterSet(values=(1, 2), marks=[pytest.mark.skip], id=None),
),
(
pytest.mark.xfail(pytest.mark.skip()((1, 2))),
ParameterSet(
values=(1, 2), marks=[pytest.mark.xfail, pytest.mark.skip], id=None
),
),
],
)
@pytest.mark.filterwarnings("ignore")
def test_parameterset_extractfrom(argval, expected):
extracted = ParameterSet.extract_from(argval)
assert extracted == expected
def test_legacy_transfer():
class FakeModule(object):
pytestmark = []
class FakeClass(object):
pytestmark = pytest.mark.nofun
@pytest.mark.fun
def fake_method(self):
pass
transfer_markers(fake_method, FakeClass, FakeModule)
# legacy marks transfer smeared
assert fake_method.nofun
assert fake_method.fun
# pristine marks dont transfer
2017-07-18 08:16:14 +08:00
assert fake_method.pytestmark == [pytest.mark.fun.mark]
class TestMarkDecorator(object):
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize(
"lhs, rhs, expected",
[
(pytest.mark.foo(), pytest.mark.foo(), True),
(pytest.mark.foo(), pytest.mark.bar(), False),
(pytest.mark.foo(), "bar", False),
("foo", pytest.mark.bar(), False),
],
)
def test__eq__(self, lhs, rhs, expected):
assert (lhs == rhs) == expected
2018-05-23 22:48:46 +08:00
@pytest.mark.parametrize("mark", [None, "", "skip", "xfail"])
def test_parameterset_for_parametrize_marks(testdir, mark):
if mark is not None:
2018-05-23 22:48:46 +08:00
testdir.makeini("[pytest]\n{}={}".format(EMPTY_PARAMETERSET_OPTION, mark))
config = testdir.parseconfig()
from _pytest.mark import pytest_configure, get_empty_parameterset_mark
2018-05-23 22:48:46 +08:00
pytest_configure(config)
2018-05-23 22:48:46 +08:00
result_mark = get_empty_parameterset_mark(config, ["a"], all)
if mark in (None, ""):
# normalize to the requested name
2018-05-23 22:48:46 +08:00
mark = "skip"
assert result_mark.name == mark
2018-05-23 22:48:46 +08:00
assert result_mark.kwargs["reason"].startswith("got empty parameter set ")
if mark == "xfail":
assert result_mark.kwargs.get("run") is False
def test_parameterset_for_parametrize_bad_markname(testdir):
with pytest.raises(pytest.UsageError):
2018-05-23 22:48:46 +08:00
test_parameterset_for_parametrize_marks(testdir, "bad")
2018-05-03 23:01:47 +08:00
def test_mark_expressions_no_smear(testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
2018-05-03 23:01:47 +08:00
import pytest
class BaseTests(object):
def test_something(self):
pass
@pytest.mark.FOO
class TestFooClass(BaseTests):
pass
@pytest.mark.BAR
class TestBarClass(BaseTests):
pass
2018-05-23 22:48:46 +08:00
"""
)
2018-05-03 23:01:47 +08:00
2018-05-23 22:48:46 +08:00
reprec = testdir.inline_run("-m", "FOO")
2018-05-03 23:01:47 +08:00
passed, skipped, failed = reprec.countoutcomes()
dlist = reprec.getcalls("pytest_deselected")
assert passed == 1
assert skipped == failed == 0
deselected_tests = dlist[0].items
assert len(deselected_tests) == 1
# keywords smear - expected behaviour
2018-05-23 22:48:46 +08:00
reprec_keywords = testdir.inline_run("-k", "FOO")
2018-05-03 23:01:47 +08:00
passed_k, skipped_k, failed_k = reprec_keywords.countoutcomes()
assert passed_k == 2
assert skipped_k == failed_k == 0