Make sure marks in subclasses don't change marks in superclasses
Fix #842
This commit is contained in:
parent
94cdec2cfe
commit
aa25fb05a9
|
@ -1,6 +1,10 @@
|
|||
2.7.3 (compared to 2.7.2)
|
||||
-----------------------------
|
||||
|
||||
- fix issue842: applying markers in classes no longer propagate this markers
|
||||
to superclasses which also have markers.
|
||||
Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- preserve warning functions after call to pytest.deprecated_call. Thanks
|
||||
Pieter Mulder for PR.
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" generic mechanism for marking and selecting python functions. """
|
||||
import inspect
|
||||
import py
|
||||
|
||||
|
||||
|
@ -253,15 +254,17 @@ class MarkDecorator:
|
|||
otherwise add *args/**kwargs in-place to mark information. """
|
||||
if args and not kwargs:
|
||||
func = args[0]
|
||||
if len(args) == 1 and (istestfunc(func) or
|
||||
hasattr(func, '__bases__')):
|
||||
if hasattr(func, '__bases__'):
|
||||
is_class = inspect.isclass(func)
|
||||
if len(args) == 1 and (istestfunc(func) or is_class):
|
||||
if is_class:
|
||||
if hasattr(func, 'pytestmark'):
|
||||
l = func.pytestmark
|
||||
if not isinstance(l, list):
|
||||
func.pytestmark = [l, self]
|
||||
else:
|
||||
l.append(self)
|
||||
mark_list = func.pytestmark
|
||||
if not isinstance(mark_list, list):
|
||||
mark_list = [mark_list]
|
||||
# always work on a copy to avoid updating pytestmark
|
||||
# from a superclass by accident
|
||||
mark_list = mark_list + [self]
|
||||
func.pytestmark = mark_list
|
||||
else:
|
||||
func.pytestmark = [self]
|
||||
else:
|
||||
|
|
|
@ -369,6 +369,45 @@ class TestFunctional:
|
|||
print (item, item.keywords)
|
||||
assert 'a' in item.keywords
|
||||
|
||||
def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir):
|
||||
p = testdir.makepyfile("""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.a
|
||||
class Base: pass
|
||||
|
||||
@pytest.mark.b
|
||||
class Test1(Base):
|
||||
def test_foo(self): pass
|
||||
|
||||
class Test2(Base):
|
||||
def test_bar(self): pass
|
||||
""")
|
||||
items, rec = testdir.inline_genitems(p)
|
||||
self.assert_markers(items, test_foo=('a', 'b'), test_bar=('a',))
|
||||
|
||||
def test_mark_decorator_baseclasses_merged(self, testdir):
|
||||
p = testdir.makepyfile("""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.a
|
||||
class Base: 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
|
||||
""")
|
||||
items, rec = testdir.inline_genitems(p)
|
||||
self.assert_markers(items, test_foo=('a', 'b', 'c'),
|
||||
test_bar=('a', 'b', 'd'))
|
||||
|
||||
def test_mark_with_wrong_marker(self, testdir):
|
||||
reprec = testdir.inline_runsource("""
|
||||
import pytest
|
||||
|
@ -477,6 +516,22 @@ class TestFunctional:
|
|||
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
|
||||
items = dict((x.name, x) for x in items)
|
||||
for name, expected_markers in expected.items():
|
||||
markers = items[name].keywords._markers
|
||||
marker_names = set([name for (name, v) in markers.items()
|
||||
if isinstance(v, MarkInfo)])
|
||||
assert marker_names == set(expected_markers)
|
||||
|
||||
|
||||
class TestKeywordSelection:
|
||||
def test_select_simple(self, testdir):
|
||||
file_test = testdir.makepyfile("""
|
||||
|
|
Loading…
Reference in New Issue