【问题标题】:Pytest - how to skip tests unless you declare an option/flag?Pytest - 除非您声明选项/标志,否则如何跳过测试?
【发布时间】:2018-05-13 13:26:20
【问题描述】:

我有一些单元测试,但我正在寻找一种方法来标记一些特定的单元测试以跳过它们,除非你在调用测试时声明一个选项。

示例: 如果我打电话给pytest test_reports.py,我希望不运行几个特定的​​单元测试。

但是如果我打电话给pytest -<something> test_reports,那么我希望我的所有测试都能运行。

我查看了@pytest.mark.skipif(condition) 标签,但无法完全弄清楚,所以不确定我是否走在正确的轨道上。这里的任何指导都会很棒!

【问题讨论】:

    标签: python unit-testing pytest


    【解决方案1】:

    我们在 conftest.py 中使用带有 addoption 的标记

    测试用例:

    @pytest.mark.no_cmd
    def test_skip_if_no_command_line():
        assert True
    

    conftest.py: 在功能中

    def pytest_addoption(parser):
        parser.addoption("--no_cmd", action="store_true",
                         help="run the tests only in case of that command line (marked with marker @no_cmd)")
    

    在功能中

    def pytest_runtest_setup(item):
        if 'no_cmd' in item.keywords and not item.config.getoption("--no_cmd"):
            pytest.skip("need --no_cmd option to run this test")
    

    pytest 调用:

        py.test test_the_marker 
        -> test will be skipped
    
       py.test test_the_marker --no_cmd
       -> test will run
    

    【讨论】:

    • 可以确认有效。 pytest_runtest_setup 也应该在 conftest.py 中。
    • 仅供参考,标记不需要与命令行参数相同。
    • 您可能还想将标记添加到 pytest.ini,否则您将收到 PytestUnknownMarkWarning 警告。添加类似markers = no_cmd
    【解决方案2】:

    pytest documentation 提供了一个很好的示例,说明如何在默认情况下跳过标记为“慢”的测试并仅使用 --runslow 选项运行它们:

    # conftest.py
    
    import pytest
    
    
    def pytest_addoption(parser):
        parser.addoption(
            "--runslow", action="store_true", default=False, help="run slow tests"
        )
    
    
    def pytest_configure(config):
        config.addinivalue_line("markers", "slow: mark test as slow to run")
    
    
    def pytest_collection_modifyitems(config, items):
        if config.getoption("--runslow"):
            # --runslow given in cli: do not skip slow tests
            return
        skip_slow = pytest.mark.skip(reason="need --runslow option to run")
        for item in items:
            if "slow" in item.keywords:
                item.add_marker(skip_slow)
    

    我们现在可以通过以下方式标记我们的测试:

    # test_module.py
    from time import sleep
    
    import pytest
    
    
    def test_func_fast():
        sleep(0.1)
    
    
    @pytest.mark.slow
    def test_func_slow():
        sleep(10)
    

    测试test_func_fast 总是被执行(例如调用pytest)。然而,“慢”函数test_func_slow 只会在调用pytest --runslow 时执行。

    【讨论】:

      【解决方案3】:

      有两种方法可以做到这一点:

      第一种方法是使用 @pytest.mark 装饰器标记函数并使用 -m 选项单独运行/跳过标记的函数。

      @pytest.mark.anytag
      def test_calc_add():
          assert True
      
      @pytest.mark.anytag
      def test_calc_multiply():
          assert True
      
      def test_calc_divide():
          assert True
      

      py.test -m anytag test_script.py 运行脚本将只运行前两个函数。

      或者以py.test -m "not anytag" test_script.py 运行将只运行第三个函数并跳过前两个函数。

      这里的“anytag”是标签的名称。它可以是任何东西。!

      第二种方法是使用-k 选项运行名称中包含公共子字符串的函数。

      def test_calc_add():
          assert True
      
      def test_calc_multiply():
          assert True
      
      def test_divide():
          assert True
      

      py.test -k calc test_script.py 运行脚本将运行函数并跳过最后一个。

      请注意,'calc' 是函数名称中存在的公共子字符串,并且名称中包含 'calc' 的任何其他函数(如 'calculate')也将运行。

      【讨论】:

        【解决方案4】:

        遵循suggested in the pytest docs 的方法,因此the answer of @Manu_CJ,肯定是这里的方法。

        我只是想展示如何调整它以轻松定义多个选项:

        canonical example given by the pytest docs 很好地突出了如何通过命令行选项添加单个标记。但是,让它添加多个标记可能并不简单,因为三个钩子 pytest_addoptionpytest_configurepytest_collection_modifyitems 都需要被调用以允许通过命令行添加单个标记选项。

        这是调整规范示例的一种方式,如果您有多个标记,例如 'flag1''flag2' 等,您希望能够通过命令行添加选项:

        # content of conftest.py
         
        import pytest
        
        # Create a dict of markers.
        # The key is used as option, so --{key} will run all tests marked with key.
        # The value must be a dict that specifies:
        # 1. 'help': the command line help text
        # 2. 'marker-descr': a description of the marker
        # 3. 'skip-reason': displayed reason whenever a test with this marker is skipped.
        optional_markers = {
            "flag1": {"help": "<Command line help text for flag1...>",
                     "marker-descr": "<Description of the marker...>",
                     "skip-reason": "Test only runs with the --{} option."},
            "flag2": {"help": "<Command line help text for flag2...>",
                     "marker-descr": "<Description of the marker...>",
                     "skip-reason": "Test only runs with the --{} option."},
            # add further markers here
        }
        
        
        def pytest_addoption(parser):
            for marker, info in optional_markers.items():
                parser.addoption("--{}".format(marker), action="store_true",
                                 default=False, help=info['help'])
        
        
        def pytest_configure(config):
            for marker, info in optional_markers.items():
                config.addinivalue_line("markers",
                                        "{}: {}".format(marker, info['marker-descr']))
        
        
        def pytest_collection_modifyitems(config, items):
            for marker, info in optional_markers.items():
                if not config.getoption("--{}".format(marker)):
                    skip_test = pytest.mark.skip(
                        reason=info['skip-reason'].format(marker)
                    )
                    for item in items:
                        if marker in item.keywords:
                            item.add_marker(skip_test)
        
        

        现在您可以在测试模块中使用optional_markers 中定义的标记:

        # content of test_module.py
        
        import pytest
        
        
        @pytest.mark.flag1
        def test_some_func():
            pass
        
        
        @pytest.mark.flag2
        def test_other_func():
            pass
        

        【讨论】:

        • 这很有用,但是从@Manu_CJ 的答案复制和粘贴时,您保留了“返回”,而不是在将其放入循环时将其更改为“继续”。目前,这是一个错误:即使只找到第一个标志,它也会运行所有测试。
        • @CodyDjango 没错。我已经反转了逻辑,所以现在不需要跳过部分迭代。
        猜你喜欢
        • 2019-02-14
        • 2013-03-28
        • 2019-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多