Python Py.test-从csv向装饰器应用变量?

Python Py.test-从csv向装饰器应用变量?,python,selenium,testing,pytest,testrail,Python,Selenium,Testing,Pytest,Testrail,请耐心听我解释我的困境,我还是Python新手,所以我的术语可能不正确。我也为这篇文章不可避免的长篇大论感到抱歉,但我会尽可能详细地解释 简要介绍: 我目前正在为一组功能基本相同的网站开发一套Selenium测试,使用py.test 使用pytest插件将测试结果上传到TestRail 测试用decorator@pytestrail.case(id)和唯一的case id进行标记 我的一个典型测试如下所示: @pytestrail.case('C100123') # associates

请耐心听我解释我的困境,我还是Python新手,所以我的术语可能不正确。我也为这篇文章不可避免的长篇大论感到抱歉,但我会尽可能详细地解释

简要介绍:

我目前正在为一组功能基本相同的网站开发一套Selenium测试,使用py.test

  • 使用pytest插件将测试结果上传到TestRail

  • 测试用decorator@pytestrail.case(id)和唯一的case id进行标记

我的一个典型测试如下所示:

@pytestrail.case('C100123')  # associates the function with the relevant TR case
@pytest.mark.usefixtures()
def test_login():
   # test code goes here
正如我前面提到的,我的目标是创建一组代码来处理我们的许多具有(实际上)相同功能的网站,因此上面示例中的硬编码装饰器将不起作用

我尝试了一种数据驱动的方法,在TestRail中有一个csv和一个测试列表及其案例ID

例如:

website1.csv:
Case ID | Test name
C100123 | test_login


website2.csv:
Case ID | Test name
C222123 | test_login
我编写的代码将使用inspect模块查找正在运行的测试的名称,找到相关的测试ID,并将其放入一个名为test_ID的变量中:

import csv
import inspect
class trp(object):
def __init__(self):
    pass


with open(testcsv) as f:  # testcsv could be website1.csv or website2.csv
    reader = csv.reader(f)
    next(reader)  # skip header
    tests = [r for r in reader]


def gettestcase(self):
    self.current_test = inspect.stack()[3][3]
    for row in trp.tests:
        if self.current_test == row[2]:
            self.test_id = (row[0])
            print(self.test_id)
            return self.test_id, self.current_test


def gettestid(self):
    self.gettestcase()
其想法是,装饰器将根据我当时使用的csv动态更改

@pytestrail.case(test_id)  # now a variable
@pytest.mark.usefixtures()
def test_login():
   trp.gettestid()
   # test code goes here
因此,如果我为website1运行test_login,装饰程序将如下所示:

@pytestrail.case('C100123')
如果我为website2运行test_登录,则装饰程序将是:

@pytestrail.case('C222123')
我为自己提出了这个解决方案感到无比自豪,并尝试了一下……但没有成功。虽然代码本身可以工作,但我会得到一个异常,因为test_id未定义(我理解为什么-
gettestcase
在decorator之后执行,所以它当然会崩溃)

我能处理这一问题的唯一其他方法是在执行任何测试代码之前应用csv和TestID。我的问题是-我如何知道如何将测试与它们的测试ID相关联?一个优雅的、最小的解决方案是什么


很抱歉这个冗长的问题。如果您需要更多的解释,我将密切关注以回答任何问题。

我相信这不起作用的原因与python读取和执行文件的方式有关。当python开始执行时,它读取链接的python文件并依次执行每一行。对于“根”缩进级别的内容(函数/类定义、装饰符、变量赋值等)这意味着它们在加载时只运行一次,以后不会再运行。在您的例子中,python解释器读取pytest testrail decorator,然后读取pytest decorator,最后读取函数定义,每次执行一次

(旁注,这就是为什么永远不要将可变对象用作函数参数默认值:)

考虑到您希望首先推断当前测试名称,然后将其与测试用例ID关联,最后将该ID与装饰器一起使用,我不确定pytest testrail的当前功能是否可能。至少,如果没有一些使用描述符之类的深奥且难以调试/维护的hack,这是不可能的

我认为实际上您有一个选择:使用不同的TestRail客户端并更新pytest结构以使用新的客户端

创建用于开始运行、更新结果和结束运行的装置需要您做更多的工作,但我认为最终您将拥有一套更可移植的测试和更好的结果报告


(*)充分披露:我是TRAW的创建者/维护者,也对testrail做出了重大贡献python

pytest
非常擅长为测试做各种元编程工作。如果我正确理解了您的问题,下面的代码将使用
pytestrail.case
marker项目根目录,创建一个名为
conftest.py
的文件,并将此代码放入其中:

import csv
from pytest_testrail.plugin import pytestrail


with open('website1.csv') as f:
    reader = csv.reader(f)
    next(reader)
    tests = [r for r in reader]


def pytest_collection_modifyitems(items):
    for item in items:
        for testid, testname in tests:
            if item.name == testname:
                item.add_marker(pytestrail.case(testid))
现在,您根本不需要使用
@pytestrail.case()
标记测试-只需编写其余代码,
pytest
将负责标记:

def test_login():
    assert True

pytest
启动时,上面的代码将读取
website1.csv
,并像在代码中一样存储测试ID和名称。在测试运行开始之前,将执行
pytest\u collection\u modifyitems
钩子,分析收集的测试-如果测试与csv文件中的名称相同,
pytest
将添加
pytestrail.case
带有测试ID的标记。

感谢您的帮助,我一定会在一周内查看这些链接!很好!刚刚试用过,这是对我来说最直接、最简单的解决方案。谢谢。很高兴我能帮上忙!