Pytest 参数化测试用例共享一个类变量

Ron*_*onu 3 python pytest

我最近开始使用 pytest 并且编写了以下课程。

import pytest
import yaml
import random

testcases = list()
class TestSample(object):
    test_yml_file = "/tmp/test_inputs.yml"

    yamlTestList = list()
    with open(test_yml_file, 'r') as testYamlFile:
        yamlTestList = yaml.load(testYamlFile)

    global testcases
    testcases = []

    for d in yamlTestList['testcases']:
        temp = dict()
        for k,v in d.items():
            temp[k] = v
        testcases.append((temp['testcase'],temp))

    last_value = 2

    @classmethod
    def setup_class(self):
        test_sample = TestSample()

    @pytest.mark.debug
    @pytest.mark.parametrize(('testname', 'testInput'), testcases)
    def test_run(self,testname,testInput):
        if last_value >= 10:
            last_value += random.randint(1,10)
Run Code Online (Sandbox Code Playgroud)

当前问题是 - 对于每个参数化测试,last_value 始终设置为 2。我们如何将先前测试用例中更改的“last_value”变量的值使用到当前测试用例中?

gua*_*unt 8

回答:

您需要lastvalue在可以调用参数化函数的外部作用域中实例化变量,而不是在测试类本身中实例化该变量。这是因为参数化如何与 pytest 配合使用。每个具有每个单独参数集的函数调用都存在于单独的作用域中,因此在您的代码中,基本上lastvalue每次在调用函数之前都将变量重置为 2。

解决方案一:

我不推荐全局变量,但是按照您的示例,这演示了我正在谈论的内容。


last_value = 0


class TestSample(object):
    testcases = [("name1", 1), ("name2", 2), ("name3", 3), ("name4", 4)]

    @pytest.mark.parametrize(('testname', 'testInput'), testcases)
    def test_run(self, testname, testInput):
        global last_value
        if last_value >= 10:
            last_value += random.randint(1, 10)
        else:
            last_value += 5

        print(last_value)
Run Code Online (Sandbox Code Playgroud)

另请注意,我在函数中添加了一个 else 子句来测试它。即使参数化的工作方式类似于单个类实例中的循环,lastvalue示例代码中的变量也永远不会更改,因为子句if last_value >= 10永远没有机会满足,因此lastvalue增量永远不会真正发生。

最佳解决方案:

我建议使用具有“类”范围的 pytest 固定装置,而不是使用全局变量。您可以在此处的 pytest 文档中阅读有关固定装置和固定装置范围的信息。

@pytest.fixture(name="sample_manager", scope="class")
def sample_manager_fixture():
    class SampleManager:
        def __init__(self):
            self.last_value = 0

    return SampleManager()


class TestSample:
    testcases = [("name1", 1), ("name2", 2), ("name3", 3), ("name4", 4)]

    def test_order(self, sample_manager):
        print(sample_manager.last_value)

    @pytest.mark.parametrize(('testname', 'testInput'), testcases)
    def test_run(self, testname, testInput, sample_manager):
        if sample_manager.last_value >= 10:
            sample_manager.last_value += random.randint(1, 10)
        else:
            sample_manager.last_value += 5

        print(sample_manager.last_value)
Run Code Online (Sandbox Code Playgroud)

当fixture函数被传递给测试函数时,它sample_manager_fixture()返回一个类实例。SampleManagerPytest 在幕后处理所有这些,因此您需要做的就是包含夹具名称(在我的例子中明确声明)作为参数。夹具范围定义了从夹具返回的每个特定对象实例的“生命周期”。因此,将固定装置的范围设置为“类”会通知 pytest 您希望特定类中的所有测试函数共享从固定装置返回的对象的相同实例。您可以从所需的装置中返回具有任何结构的任何对象,因此它们是管理测试中数据的真正强大的工具。