Python - 除非指定,否则使用 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) ,但不确定如何覆盖自动使用变量

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

原文由 shane armstrong 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 756
2 个回答

有几种方法可以处理这个问题,但我将介绍我在 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时,你可以通过做 pytest test/unit 来调用它—如果你 只想 运行单元测试(即只有 test_something*.py 文件),或者 pytest test/integration只想 运行集成测试(即只 test_optional.py ),或者 pytest test 如果你想运行 所有 测试。因此,默认情况下,您可以只运行 pytest test/unit

我建议将这些调用包装在某种脚本中。我更喜欢 make 因为它对这种类型的包装很强大。然后你可以说 make test 它只运行你的默认(快速)测试套件,或者 make test_all ,它会运行所有测试(可能会或可能不会很慢)。

您可以包装的示例 Makefile:

 .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

2) 使用 @pytest.mark.skipif 装饰器明智地标记您的测试,但使用环境变量作为触发器

我不太喜欢这个解决方案,对我来说感觉有点随意(很难说出在任何给定的情况下正在运行哪一组测试 pytest 运行)。但是,您可以做的是定义一个环境变量,然后将该环境变量绑定到模块中以检测您是否要运行所有测试。环境变量依赖于 shell,但我会假装你有一个 bash 环境,因为它是一个流行的 shell。

你可以做 export TEST_LEVEL="unit" 只是为了快速单元测试(所以这将是你的默认值),或者 export TEST_LEVEL="all" 为你所有的测试。然后在你的测试文件中,你可以像这样做你最初想做的事情:

 import os

...

@pytest.mark.skipif(os.environ["TEST_LEVEL"] == "unit")
def test_scrape_website():
  ...

注意: 将测试级别命名为“单元”和“集成”是无关紧要的。您可以随意命名它们。您还可以有很多级别(比如夜间测试或性能测试)。

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

喂!

原文由 Matt Messersmith 发布,翻译遵循 CC BY-SA 4.0 许可协议

文档准确描述了您的问题: 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

原文由 xverges 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题