【问题标题】:Pytest not able to skip testcase in a class via marker skipifPytest 无法通过标记 skipif 跳过类中的测试用例
【发布时间】:2019-01-16 13:24:45
【问题描述】:

我正在使用 pytest 框架并希望根据某些条件跳过测试用例。下面的代码没有跳过测试用例。

import pytest
class TEST:
    @pytest.fixture(scope="function", autouse=True)
    def func_fixture(self):
        return "fail"

    @pytest.mark.skipif("self.func_fixture=='fail'")
    def test_setting_value(self):
        print("Hello I am in testcase")

运行时,表示执行了 0 个测试用例。

这个func_fixture 对测试套件非常重要。它在开始测试之前执行许多先决条件。

如果我删除类,并使用相同的语法添加其余函数(删除 self 之后),它就可以工作。不知道为什么它在课堂上失败

【问题讨论】:

    标签: python unit-testing testing pytest


    【解决方案1】:

    TL;DR

    你可以跳过测试本身的条件;跳到答案末尾的Suggestion。使用您的示例代码,无法通过 skipIf 标记跳过测试。


    首先我要明确一点:如果你定义了一个返回值的autouse fixturefoo,并不意味着测试类会自动变成属性foo

    class TestCls:
    
        @pytest.fixture(autouse=True)
        def fix(self):
            return 'foo'
    
        def test_fix(self):
            assert self.fix == 'foo'
    

    运行此测试将失败:

        def test_fix(self):
    >       assert self.fix == 'foo'
    E       AssertionError: assert <bound method TestCls.fix of <test_bacon.TestCls object at 0x105670cf8>> == 'foo'
    E        +  where <bound method TestCls.fix of <test_bacon.TestCls object at 0x105670cf8>> = <test_bacon.TestCls object at 0x105670cf8>.fix
    

    那是因为固定装置不是这样工作的。如果一个fixture正在返回一些东西,你可以在测试函数中添加一个与fixture同名的参数来访问它的返回值。这是类的问题,因为您不能将夹具作为参数传递给测试类方法;总的来说,pytest 中对测试classes的支持是有限的。不知道你为什么需要上课;您可以仅使用夹具在多个测试功能之间完美共享状态。 pytest 的全部目的是使测试类过时,每个测试方法都可以重新设计为测试函数。

    如果您需要在一个类中的测试之间共享一个值,您需要将它分配给夹具中测试类实例的某个属性。下面的例子会通过:

    class TestCls:
    
        @pytest.fixture(autouse=True)
        def fix(self):
            self._fix = 'foo'
    
        def test_fix(self):
            assert self._fix == 'foo'
    

    因此,实际上返回某些东西的夹具在测试类中是没有用的,因为没有办法将夹具作为参数传递给测试类方法。

    不知道为什么上课失败

    这是因为在测试集合上评估标记以过滤出应该执行的测试,并且仅在测试收集并准备好运行之后执行夹具。这意味着你想要的东西是不可能的。您将无法将任何夹具结果传递给skipif 标记,因为尚未评估任何夹具。查看执行顺序:

    @pytest.fixture(autouse=True)
    def myfixture():
        print('myfixture called')
    
    
    class TestCls:
    
        @pytest.fixture(autouse=True)
        def myclassfixture(self):
            print('TestCls.myclassfixture called')
    
        @pytest.mark.skipif('print(os.linesep, "TestCls.test_spam skipif called")', reason='something')
        def test_spam(self):
            print('TestCls.test_spam called')
    

    输出:

    TestCls.test_spam skipif called
    myfixture called
    TestCls.myclassfixture called
    TestCls.test_spam called
    

    另请注意,skipif 中没有测试类实例,只有在模块级别定义的内容(类、函数和全局变量):

    class Tests:
    
        @pytest.mark.skipif('print(os.linesep, "globals:", globals().keys(), os.linesep, "locals:", locals().keys())', reason='something')
        def test_spam(self):
            pass
    

    输出:

    test_spam.py::TestCls::test_spam
     globals: dict_keys(['os', 'sys', 'platform', 'config', '__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', '@py_builtins', '@pytest_ar', 'pytest', 'myfixture', 'TestCls'])
     locals: dict_keys(['os', 'sys', 'platform', 'config', '__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', '@py_builtins', '@pytest_ar', 'pytest', 'myfixture', 'TestCls'])
    

    建议

    除了skip/skipIf 标记之外,您还可以在测试本身内显式跳过测试:

    class TestCls:
    
        @pytest.fixture(autouse=True)
        def myfixture(self):
            self.status = 'fail'
    
        def test_eggs(self):
            if self.status == 'fail':
                pytest.skip('self.status is set to "fail"')
            assert False
    

    如果运行,则跳过测试:

    test_eggs.py::TestCls::test_eggs SKIPPED
    

    【讨论】:

    • 感谢您的解决方案。如果我想将任何测试用例中的状态值更新为 'pass' ,我该怎么做?
    • 在一个测试用例中,我将值更新为好像 some_condition 然后 self.status='pass' ,但是当我检查下一个测试用例时,好像 self.status == 'pass',它仍然“失败”
    【解决方案2】:

    首先,根据Conventions for Python test discovery,类名应该以Test开头:

    从这些文件中,收集测试项目:

    • test_ 前缀测试函数或 类外的方法
    • Test 前缀测试类中的 test_ 前缀测试函数或方法(没有 __init__ 方法)
    class Test...:
    

    第二,@pytest.mark.skipif("func_fixture=='fail'")中的func_fixture是一个函数,而不是函数调用的返回值。 (我不知道如何在pytest.mark.skipif(..) 中使用fixture 值;参见this answer to see how to use fixture in expression for skipif)。

    import pytest
    
    
    @pytest.fixture
    def func_fixture():
        return 'fail'
    
    
    skip_by_fixture = pytest.mark.skipif("func_fixture() == 'fail'")
    
    class TestSetting:
        @skip_by_fixture
        def test_setting_value(self):
            print("Hello I am in testcase")
    

    【讨论】:

    • 您已将夹具 func_fixture 移出课堂。如果我想把这个夹具放在课堂上怎么办
    • 您的代码正在运行,但我希望在类中使用fixture func_fixture。
    • @Nitesh,对不起,我不知道如何将fixture值注入到skipif的表达式中/使用实例方法作为fixture。
    • @Nitesh,这个怎么样:pastebin.com/Hw2C1kKafunc_fixture 不是一个夹具,只是一个静态方法)
    • 现在 func_fixture 不是一个夹具。我正在执行我在夹具中执行的操作。这个想法是,如果不满足先决条件,我不想执行测试用例
    【解决方案3】:

    Pytest 提供了一个功能来管理测试的依赖关系。您可以创建单独的测试并设置对连续测试的依赖关系。您需要安装 pytest-dependency。

    pip install pytest-dependency

    例子:

    class TestExample(object):
    
        @pytest.mark.dependency()
        def test_func(self):
            assert False
    
        @pytest.mark.dependency(depends=["TestExample::test_func"])
        def test_setting_value(self):
            print("Hello I am in testcase")
    

    【讨论】:

    • 你的回答不是很清楚。你能解释一下吗
    • 您可以在单独的测试中断言您在夹具中使用的条件,例如 test_func(在上面的示例中)。如果test_func 通过了,那么只有test_setting_value 会被执行,否则会跳过那个测试,因为我们已经在这个测试上设置了test_func 的依赖。
    • test_func 实际上是一个夹具,它在测试用例开始之前执行某些步骤。 @pytest.mark.dependency 也可以与固定装置合并吗?
    • 它只允许将一些测试标记为依赖于其他测试。如果任何依赖项确实失败或已被跳过,则这些测试将被跳过。但是在您的情况下,您可以将您的夹具替换为将首先执行的测试并设置对其他测试的依赖关系。 [pytest-dependency.readthedocs.io/en/latest/…
    【解决方案4】:

    首先,只有TEST 的类名将不起作用。

    其次,fixture 有时很痛苦......,我会使用简单的方法来检查并返回条件以跳过(失败)或不。

    import pytest
    import unittest
    
    
    class TestSkipWithoutFixture(unittest.TestCase):
    
        def fixture_sometimes_suck():
            return "fail"
    
        @pytest.mark.skipif(fixture_sometimes_suck() == 'fail', reason='just skip')
        def test_setting_value(self):
            print("Hello I am in testcase")
    
        def test_to_pass(self):
            pass
    

    测试结果pytest -v tests/test_skip.py

    platform linux2 -- Python 2.7.14, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- /usr/bin/python2
    
    collected 2 items                                                                                                                                 
    
    tests/test_skip.py::TestSkipWithoutFixture::test_setting_value SKIPPED
    tests/test_skip.py::TestSkipWithoutFixture::test_to_pass PASSED
    

    【讨论】:

    • 如果在 'test_setting_value' 中有条件检查,如果 a == 1,则应跳过其余测试用例。那可能吗 ? stackoverflow.com/questions/62847026/…
    • @StackGuru,fasetru 的回答与您的问题相关,没有顺序在 TestClass 中首先运行测试,因此条件必须在测试类之外,如果首选夹具,如果一个类中所有测试的条件相同,但是,skipif 夹具必须位于每个测试函数的顶部
    • 如何在其中一个测试用例中根据某些标准将“失败”更新为“通过”。稍后相应地添加标记skipif。
    猜你喜欢
    • 2016-03-24
    • 2013-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-27
    • 1970-01-01
    • 2015-10-10
    • 2020-08-19
    相关资源
    最近更新 更多