Make sure marks in subclasses don't change marks in superclasses

Fix #842
This commit is contained in:
Bruno Oliveira 2015-07-15 21:03:30 -03:00
parent 94cdec2cfe
commit aa25fb05a9
3 changed files with 70 additions and 8 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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("""