Automated tests pytest:测试类上的标记覆盖测试函数上的相同标记

Automated tests pytest:测试类上的标记覆盖测试函数上的相同标记,automated-tests,pytest,Automated Tests,Pytest,我正在使用pytest.mark进行测试。但是,如果我在类和类内的测试上使用相同的标记,则当相同的KWARG同时用于这两个测试时,该类的标记将覆盖函数上的标记 import pytest animal = pytest.mark.animal @animal(species='croc') # Mark the class with a kwarg class TestClass(object): @animal(species='hippo') # Mark the fun

我正在使用pytest.mark进行测试。但是,如果我在类和类内的测试上使用相同的标记,则当相同的KWARG同时用于这两个测试时,该类的标记将覆盖函数上的标记

import pytest

animal = pytest.mark.animal


@animal(species='croc')  # Mark the class with a kwarg
class TestClass(object):

    @animal(species='hippo')  # Mark the function with new kwarg
    def test_function(self):
        pass


@pytest.fixture(autouse=True)  # Use a fixture to inspect my function
def animal_inspector(request):
    print request.function.animal.kwargs  # Show how the function object got marked


# prints {'species': 'croc'} but the function was marked with 'hippo'

我的河马去哪了?我怎样才能把它弄回来?

不幸的是,这里有各种各样的河马,我猜你遇到了其中一种。我发现的那些与子类化有关,但你没有这样做。

不幸的是,有各种各样的,我猜你遇到了其中的一种。我发现的那些与子类化有关,但你在那里没有这样做。

所以我一直在挖掘pytest代码,并找出了发生这种情况的原因。函数上的标记在导入时应用于函数,但类和模块级别的标记在测试集合之前不会应用于函数级别。函数标记首先出现,并将它们的KWARG添加到函数中。然后类标记覆盖任何相同的KWARG,模块标记进一步覆盖任何匹配的KWARG

我的解决方案是简单地创建我自己的修改过的MarkDecorator,在将Kwarg添加到标记之前过滤Kwarg。基本上,首先设置的kwarg值(似乎总是由函数装饰器设置的)将始终是标记上的值。理想情况下,我认为应该将此功能添加到MarkInfo类中,但由于我的代码没有创建该类的实例,因此我使用了我创建的实例:MarkDecorator。请注意,我只更改了源代码中的两行代码(关于键\u到\u add的位)

来自_pytest.mark import istestfunc,MarkInfo
进口检验
类TestMarker(对象):#修改的MarkDecorator类
def u u init u u;(self,name,args=None,kwargs=None):
self.name=名称
self.args=args或()
self.kwargs=kwargs或{}
@财产
def标记名(自身):
返回self.name#用于向后兼容(2.4.1有此属性)
定义报告(自我):
d=自我记录副本()
name=d.pop('name')
返回“”%(名称,d)
定义调用(self,*args,**kwargs):
“”“如果传递了单个可调用参数:用标记信息装饰它。”。
否则,在适当位置添加*args/**kwargs以标记信息。”“”
如果是args而不是kwargs:
func=args[0]
is_class=inspect.isclass(func)
如果len(args)=1且(istestfunc(func)或为_类):
如果是大学级别:
如果hasattr(func,“pytestmark”):
mark_list=func.pytestmark
如果不存在(标记列表,列表):
标记列表=[标记列表]
标记列表=标记列表+[自身]
func.pytestmark=标记列表
其他:
func.pytestmark=[self]
其他:
holder=getattr(func,self.name,None)
如果持有人为无:
holder=MarkInfo(
self.name、self.args、self.kwargs
)
setattr(func、self.name、holder)
其他:
#不要设置标记上已存在的标记
keys_to_add={key:key的值,self.kwargs.items()中的值,如果key不在holder.kwargs}
holder.add(self.args,key\u to\u add)
返回函数
kw=self.kwargs.copy()
功率更新(kwargs)
args=self.args+args
返回self.\uuuu类(self.name,args=args,kwargs=kw)
#创建我的标记实例。注意:必须导入修改后的标记类才能使用
animal=TestMarker(name='animal')
#将其应用于类和函数
@动物(物种='croc')#用夸格标记班级
类TestClass(对象):
@动物(物种=‘河马’)#用新kwarg标记功能
def测试_功能(自身):
通过
#现在打印{‘物种’:‘河马’}耶!

所以我一直在挖掘pytest代码,并找出了发生这种情况的原因。函数上的标记在导入时应用于函数,但类和模块级别的标记在测试集合之前不会应用于函数级别。函数标记首先出现,并将它们的KWARG添加到函数中。然后类标记覆盖任何相同的KWARG,模块标记进一步覆盖任何匹配的KWARG

我的解决方案是简单地创建我自己的修改过的MarkDecorator,在将Kwarg添加到标记之前过滤Kwarg。基本上,首先设置的kwarg值(似乎总是由函数装饰器设置的)将始终是标记上的值。理想情况下,我认为应该将此功能添加到MarkInfo类中,但由于我的代码没有创建该类的实例,因此我使用了我创建的实例:MarkDecorator。请注意,我只更改了源代码中的两行代码(关于键\u到\u add的位)

来自_pytest.mark import istestfunc,MarkInfo
进口检验
类TestMarker(对象):#修改的MarkDecorator类
def u u init u u;(self,name,args=None,kwargs=None):
self.name=名称
self.args=args或()
self.kwargs=kwargs或{}
@财产
def标记名(自身):
返回self.name#用于向后兼容(2.4.1有此属性)
定义报告(自我):
d=自我记录副本()
name=d.pop('name')
返回“”%(名称,d)
定义调用(self,*args,**kwargs):
“”“如果传递了单个可调用参数:用标记信息装饰它。”。
否则,在适当位置添加*args/**kwargs以标记信息。”“”
如果是args而不是kwargs:
func=args[0]
is_class=inspect.isclass(func)
如果len(args)=1且(istestfunc(func)或为_类):
如果是大学级别:
如果hasattr(func,“pytestmark”):
马克里斯
from _pytest.mark import istestfunc, MarkInfo
import inspect


class TestMarker(object):  # Modified MarkDecorator class
    def __init__(self, name, args=None, kwargs=None):
        self.name = name
        self.args = args or ()
        self.kwargs = kwargs or {}

    @property
    def markname(self):
        return self.name # for backward-compat (2.4.1 had this attr)

    def __repr__(self):
        d = self.__dict__.copy()
        name = d.pop('name')
        return "<MarkDecorator %r %r>" % (name, d)

    def __call__(self, *args, **kwargs):
        """ if passed a single callable argument: decorate it with mark info.
            otherwise add *args/**kwargs in-place to mark information. """
        if args and not kwargs:
            func = args[0]
            is_class = inspect.isclass(func)
            if len(args) == 1 and (istestfunc(func) or is_class):
                if is_class:
                    if hasattr(func, 'pytestmark'):
                        mark_list = func.pytestmark
                        if not isinstance(mark_list, list):
                            mark_list = [mark_list]
                        mark_list = mark_list + [self]
                        func.pytestmark = mark_list
                    else:
                        func.pytestmark = [self]
                else:
                    holder = getattr(func, self.name, None)
                    if holder is None:
                        holder = MarkInfo(
                            self.name, self.args, self.kwargs
                        )
                        setattr(func, self.name, holder)
                    else:
                        # Don't set kwargs that already exist on the mark
                        keys_to_add = {key: value for key, value in self.kwargs.items() if key not in holder.kwargs}
                        holder.add(self.args, keys_to_add)
                return func
        kw = self.kwargs.copy()
        kw.update(kwargs)
        args = self.args + args
        return self.__class__(self.name, args=args, kwargs=kw)


# Create my Mark instance. Note my modified mark class must be imported to be used
animal = TestMarker(name='animal')

# Apply it to class and function
@animal(species='croc')  # Mark the class with a kwarg
class TestClass(object):

    @animal(species='hippo')  # Mark the function with new kwarg
    def test_function(self):
        pass

# Now prints {'species': 'hippo'}  Yay!