【问题标题】:How can I repeat each test multiple times in a py.test run?如何在 py.test 运行中多次重复每个测试?
【发布时间】:2014-03-12 22:27:00
【问题描述】:

我想按顺序运行每个选定的 py.test 项目任意次数。
我没有看到任何标准的 py.test 机制来执行此操作。

我试图在pytest_collection_modifyitems() 挂钩中执行此操作。我修改了传入的项目列表,以多次指定每个项目。测试项目的第一次执行按预期工作,但这似乎给我的代码带来了一些问题。

此外,我希望每次运行都有一个唯一的测试项目对象,因为我在各种报告代码中使用 id(项目)作为键。不幸的是,我找不到任何 py.test 代码来复制测试项,copy.copy() 不起作用,copy.deepcopy() 出现异常。

谁能建议一个多次执行测试的策略?

【问题讨论】:

    标签: python pytest


    【解决方案1】:

    一种可能的策略是参数化相关测试,但不明确使用参数。

    例如:

    @pytest.mark.parametrize('execution_number', range(5))
    def run_multiple_times(execution_number):
        assert True
    

    上述测试应该运行五次。

    查看参数化文档:https://pytest.org/latest/parametrize.html

    【讨论】:

    • 这将按照测试文件中指定的次数执行测试。它没有达到我的目标,即按照命令行选项指定的任意次数执行测试。
    • 抱歉,我完全误解了你的问题。我想我已经弄清楚了如何做你想做的事,我将把它作为另一个答案添加(因为这个完全错误:)。
    • 我很高兴这个答案在这里。我认为它增加了未来用户寻找可能解决方案的背景。
    • 如果你想“硬编码”测试应该执行的次数,这是一个很好的解决方案
    • 这个特别酷的地方在于能够使用 n 来表示第 n 次 {X} 发生。不过,这只是创建了一个参数测试,因此您可以使用任何一维数据源(例如数组)来使测试更具可读性。我刚刚获取了一系列预设名称,这会自动插入它们,以便您知道哪个精确的名称失败了。非常好的测试可读性功能
    【解决方案2】:

    pytest 模块pytest-repeat 就是为此目的而存在的,我建议尽可能使用模块,而不是自己重新实现它们的功能。

    要使用它,只需将pytest-repeat 添加到您的requirements.txtpip install pytest-repeat,然后使用--count n 执行您的测试。

    【讨论】:

    【解决方案3】:

    为了多次运行每个测试,我们将在生成测试时以编程方式参数化每个测试。

    首先,让我们添加解析器选项(在您的一个 conftest.py 中包含以下内容):

    def pytest_addoption(parser):
        parser.addoption('--repeat', action='store',
            help='Number of times to repeat each test')
    

    现在我们添加一个“pytest_generate_tests”钩子。这就是神奇发生的地方。

    def pytest_generate_tests(metafunc):
        if metafunc.config.option.repeat is not None:
            count = int(metafunc.config.option.repeat)
    
            # We're going to duplicate these tests by parametrizing them,
            # which requires that each test has a fixture to accept the parameter.
            # We can add a new fixture like so:
            metafunc.fixturenames.append('tmp_ct')
    
            # Now we parametrize. This is what happens when we do e.g.,
            # @pytest.mark.parametrize('tmp_ct', range(count))
            # def test_foo(): pass
            metafunc.parametrize('tmp_ct', range(count))
    

    在没有重复标志的情况下运行:

    (env) $ py.test test.py -vv
    ============================= test session starts ==============================
    platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python
    collected 2 items 
    
    test.py:4: test_1 PASSED
    test.py:8: test_2 PASSED
    
    =========================== 2 passed in 0.01 seconds ===========================
    

    使用重复标志运行:

    (env) $ py.test test.py -vv --repeat 3
    ============================= test session starts ==============================
    platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python
    collected 6 items 
    
    test.py:4: test_1[0] PASSED
    test.py:4: test_1[1] PASSED
    test.py:4: test_1[2] PASSED
    test.py:8: test_2[0] PASSED
    test.py:8: test_2[1] PASSED
    test.py:8: test_2[2] PASSED
    
    =========================== 6 passed in 0.01 seconds ===========================
    

    进一步阅读:

    【讨论】:

    • 虽然此方法可能有效,但我使用您的建议找到了一种更简单的方法,不需要任何参数化参数等。我将发布它并接受它作为答案。感谢您的建议;它直接引导我找到我的解决方案。
    • 正在做一个新项目,所以我来这里是想弄清楚我以前是怎么做的。我现在明白这个解决方案不需要我为每个测试声明一个夹具参数。所以我选择这个答案是正确的,我在我的新项目中使用你的策略。谢谢!
    • 如何按顺序重复整个测试文件?这里 test 1 运行了 3 次,那么运行 1,2,3(按顺序)3 次呢?
    【解决方案4】:

    根据 Frank T 的建议,我在 pytest_generate_tests() 标注中找到了一个非常简单的解决方案:

    parser.addoption ('--count', default=1, type='int', metavar='count', help='Run each test the specified number of times')
    
    def pytest_generate_tests (metafunc):
        for i in range (metafunc.config.option.count):
            metafunc.addcall()
    

    现在执行py.test --count 5 会使每个测试在测试会话中执行五次。

    而且它不需要对我们现有的任何测试进行任何更改。

    【讨论】:

    • metafunc.addcall() 已被弃用,这就是为什么我在我的实现中更喜欢 .parametrize() 的原因。另见:pytest.org/latest/…
    • 您的解决方案要求我为我的 1,000 个现有测试中的每一个添加一个参数/funcarg,这是我不想做的。可能有一个 .parametrize() 等效于我的简单解决方案,但我无法弄清楚。
    • 正在做一个新项目,所以我来这里是想弄清楚我以前是怎么做的。我现在明白您的解决方案不需要我为每个测试声明一个夹具参数。所以我选择你原来的答案是正确的,我在我的新项目中使用你的策略。谢谢!
    【解决方案5】:

    根据我在这里看到的情况,鉴于我已经在pytest_collection_modifyitems 中进行了一些测试过滤,我选择的方法如下。在conftest.py

    def pytest_addoption(parser):
        parser.addoption ('--count', default=1, type='int', metavar='count', help='Run each test the specified number of times')
    
    
    def pytest_collection_modifyitems(session, config, items):
        count = config.option.count
        items[:] = items * count  # add each test multiple times
    

    【讨论】:

    • 为避免出现警告,type='int' 应为 type=int
    【解决方案6】:

    虽然pytest-repeat(最受欢迎的答案)不适用于unittest 类测试,但pytest-flakefinder 可以:

    pip install pytest-flakefinder
    pytest --flake-finder --flake-runs=5 tests...
    

    在找到test-flakefinder 之前,我写了一个脚本来做类似的事情。你可以找到它here。脚本的顶部包含如何运行它的说明。

    【讨论】:

    • 感谢pytest-repeat
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    • 2011-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-27
    相关资源
    最近更新 更多