Python-除非指定,否则使用pytest跳过测试

时间:2018-09-09 15:48:03

标签: python python-3.x testing pytest

背景

我正在使用pytest来测试将数据推送到数据库的网络抓取工具。该类仅提取html并将html推送到要稍后解析的数据库。我的大多数测试都使用伪数据表示html。

问题

我想做一个测试,从网站上刮掉一个网页,但是我希望测试自动关闭,除非指定。如果您有一个昂贵或耗时的测试不想一直运行,那么可能会发生类似的情况。

期望的解决方案

我期望某种标记可以抑制测试,除非我让pytest运行所有抑制的测试,但是我没有在文档中看到它。

我做了什么

  • 我当前正在使用跳过标记并将其注释掉。
  • 试图使用skipif标记,并使用命令提示符pytest test_file.py 1和以下测试文件中的以下代码使用此命令为python脚本提供参数。问题是,当我尝试为test_file提供参数时,pytest期望它是另一个文件名,因此我收到一条错误消息:“在0.00秒内没有运行测试,错误:找不到文件:1”

    if len(sys.argv) == 1:
      RUN_ALL_TESTS = False
    else:
      RUN_ALL_TESTS = True
    ...
    # other tests
    ...
    
    @pytest.mark.skipif(RUN_ALL_TESTS)
    def test_scrape_website():
      ...
    
  • 我也许可以将测试视为一种夹具并使用@pytest.fixture(autouse=False),但不确定如何覆盖autouse变量

  • How to skip a pytest using an external fixture?中提出了类似的解决方案,但是这种解决方案似乎比我需要的解决方案更为复杂。

4 个答案:

答案 0 :(得分:3)

文档准确描述了您的问题:https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option。从那里复制:

  

这是一个conftest.py文件,在其中添加了--runslow命令行选项   控制跳过pytest.mark.slow标记测试:

# content of conftest.py

import pytest


def pytest_addoption(parser):
    parser.addoption(
        "--runslow", action="store_true", default=False, help="run slow tests"
    )


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)
  

我们现在可以编写这样的测试模块:

# content of test_module.py
import pytest


def test_func_fast():
    pass


@pytest.mark.slow
def test_func_slow():
    pass

答案 1 :(得分:3)

有两种方法可以解决此问题,但我将介绍在Python基准中见过的两种常见方法。

1)通过将“可选”测试放在另一个目录中来分离测试。

不确定项目的布局是什么样,但是您可以执行以下操作(仅测试目录很重要,其余只是玩具示例布局):

README.md
setup.py
requirements.txt
test/
    unit/
        test_something.py
        test_something_else.py
    integration/
        test_optional.py
application/
    __init__.py
    some_module.py

然后,在调用pytest时,如果要运行 just 单元测试(即仅pytest test/unit文件)或{{ 1}}如果要运行 just 集成测试(即仅test_something*.py),或者pytest test/integration如果要运行 all 测试。因此,默认情况下,您可以只运行test_optional.py

我建议将这些调用包装在某种脚本中。我更喜欢pytest test,因为它对于这种包装非常有用。然后,您可以说pytest unit/test,它只运行您的默认(快速)测试套件或make,它将运行所有测试(可能很慢,也可能不会很慢)。

示例包装文件,您可以使用:

make test

2)使用make test_all装饰器明智地标记测试,但使用环境变量作为触发器

我不太喜欢这种解决方案,这让我感到有些无所事事(很难确定在任何给定.PHONY: all clean install test test_int test_all uninstall all: install clean: rm -rf build rm -rf dist rm -rf *.egg-info install: python setup.py install test: install pytest -v -s test/unit test_int: install pytest -v -s test/integration test_all: install pytest -v -s test uninstall: pip uninstall app_name 运行中正在运行哪组测试)。但是,您可以做的是定义一个环境变量,然后将该环境变量连接到模块中以检测是否要运行所有测试。环境变量取决于外壳,但是我假装您有一个bash环境,因为它是流行的外壳。

您可以仅针对快速的单元测试执行@pytest.mark.skipif(因此这将是您的默认设置),或者针对所有测试执行pytest。然后,在测试文件中,您可以执行以下操作:

export TEST_LEVEL="unit"

注意:将测试级别命名为“单位”和“集成”无关。您可以随意命名。您还可以具有许多级别(例如每晚测试或性能测试)。

此外,我认为选项1是最好的选择,因为它不仅清楚地允许测试分离,而且还可以为测试的含义和表示增加语义和清晰度。但是软件中没有“一刀切”的解决方案,您必须根据自己的具体情况来决定采用哪种方法。

HTH!

答案 2 :(得分:3)

一个非常简单的解决方案是使用-k参数。您可以使用-k参数取消选择某些测试。 -k尝试将其参数与测试名称或标记的任何部分匹配。您可以使用not反转匹配(也可以使用布尔运算符andor )。因此,-k 'not slow'跳过名称中具有“ slow”,名称中具有“ slow”的标记或其类/模块名称包含“ slow”的测试。

例如,给定此文件:

import pytest

def test_true():
    assert True

@pytest.mark.slow
def test_long():
    assert False

def test_slow():
    assert False

运行时:

pytest -k 'not slow'

输出类似:(请注意,两个失败的测试都与过滤器匹配,因此被跳过了)

============================= test session starts =============================
platform win32 -- Python 3.5.1, pytest-3.4.0, py-1.5.2, pluggy-0.6.0
rootdir: c:\Users\User\Documents\python, inifile:
collected 3 items

test_thing.py .                                                          [100%]

============================= 2 tests deselected ==============================
=================== 1 passed, 2 deselected in 0.02 seconds ====================

由于热切的匹配,您可能需要执行一些操作,例如将所有单元测试放在名为unittest的目录中,然后将慢速单元测试标记为slow_unittest(以便意外地匹配碰巧名字有点慢)。然后,您可以使用-k 'unittest and not slow_unittest'来匹配所有快速单元测试。

More pytest example marker usage

答案 3 :(得分:1)

形成一个小类,以便在多个mark / cli选项上重复使用@xverges代码;

@dataclass
class TestsWithMarkSkipper:
    ''' Util to skip tests with mark, unless cli option provided. '''

    test_mark: str
    cli_option_name: str
    cli_option_help: str

    def pytest_addoption_hook(self, parser):
        parser.addoption(
            self.cli_option_name,
            action="store_true",
            default=False,
            help=self.cli_option_help,
        )

    def pytest_collection_modifyitems_hook(self, config, items):
        if not config.getoption(self.cli_option_name):
            self._skip_items_with_mark(items)

    def _skip_items_with_mark(self, items):
        reason = "need {} option to run".format(self.cli_option_name)
        skip_marker = pytest.mark.skip(reason=reason)
        for item in items:
            if self.test_mark in item.keywords:
                item.add_marker(skip_marker)

用法示例(必须放在conftest.py中):

slow_skipper = TestsWithMarkSkipper(
    test_mark='slow',
    cli_option_name="--runslow",
    cli_option_help="run slow tests",
)
pytest_addoption = slow_skipper.pytest_addoption_hook
pytest_collection_modifyitems = slow_skipper.pytest_collection_modifyitems_hook