集成测试#

conda 中的集成测试从较高层级测试应用程序,其中每个测试都可能涵盖代码的大部分。这些测试也可能使用本地文件系统和/或执行网络调用。在以下部分,我们将介绍一些关于这些测试如何运作的具体示例。当你编写自己的集成测试时,这些例子可以作为良好的起点。

conda_cli 固定装置:运行 CLI 级别测试#

CLI 级别测试是你能编写的最高级别的集成测试。这意味着测试中的代码以你在命令行中运行的方式执行。例如,你可能希望编写一个测试来确认在成功运行 conda create 后创建了一个环境。这样的测试看起来像这样

用于 conda create 的集成测试#
 1import json
 2from pathlib import Path
 3
 4from conda.testing import CondaCLIFixture
 5
 6
 7def test_conda_create(conda_cli: CondaCLIFixture, tmp_path: Path):
 8    # setup, create environment
 9    out, err, code = conda_cli("create", "--prefix", tmp_path, "--yes")
10
11    assert f"conda activate {tmp_path}" in out
12    assert not err  # no errors
13    assert not code  # success!
14
15    # verify everything worked using the `conda env list` command
16    out, err, code = conda_cli("env", "list", "--json")
17
18    assert any(
19        tmp_path.samefile(path)
20        for path in json.loads(out).get("envs", [])
21    )
22    assert not err  # no errors
23    assert not code  # success!
24
25    # cleanup, remove environment
26    out, err, code = conda_cli("remove", "--all", "--prefix", tmp_path)
27
28    assert out
29    assert not err  # no errors
30    assert not code  # success!

让我们分解上面代码片段中究竟发生了什么

首先,我们依赖于一个固定装置(conda_cli),它允许我们使用当前正在运行的进程来运行命令。这比通过子进程运行 CLI 测试效率更高、速度更快。

在测试本身中,我们首先通过实际运行 conda create 来创建一个新的环境。此函数返回命令的标准输出、标准错误和退出代码。这使我们能够进行检查,以确定命令是否成功运行。

测试的第二部分再次使用 conda_cli 固定装置来调用 conda env list。这一次,我们传递了 --json 标志,它允许捕获 JSON,我们可以更好地解析并更容易地检查。然后我们断言我们刚刚创建的环境是否确实在可用的环境列表中。

最后,我们销毁了刚刚创建的环境,并确保标准错误和退出代码是我们期望的。

警告

在测试运行后,最好在可能的情况下使用临时目录(例如 tmp_path)进行自动清理。否则,请记住删除测试期间创建的任何内容,因为这些内容将在其他测试运行时存在,并且可能导致意外的竞争条件。

tmp_env 固定装置:创建临时环境#

tmp_env 固定装置是为测试创建临时环境的便捷方法

使用 numpy 创建环境的集成测试#
 1from conda.testing import CondaCLIFixture, TmpEnvFixture
 2
 3
 4def test_environment_with_numpy(
 5    tmp_env: TmpEnvFixture,
 6    conda_cli: CondaCLIFixture,
 7):
 8    with tmp_env("numpy") as prefix:
 9        out, err, code = conda_cli("list", "--prefix", prefix)
10
11        assert out
12        assert not err  # no error
13        assert not code  # success!

path_factory 固定装置:创建唯一(不存在)的路径#

path_factory 固定装置扩展了 pytest 的 tmp_path 固定装置,以提供唯一、未使用的路径。这使得在测试中更容易生成新的路径

重命名环境的集成测试#
 1from conda.testing import (
 2    CondaCLIFixture,
 3    PathFactoryFixture,
 4    TmpEnvFixture,
 5)
 6
 7
 8def test_conda_rename(
 9    path_factory: PathFactoryFixture,
10    tmp_env: TmpEnvFixture,
11    conda_cli: CondaCLIFixture,
12    tmp_path: Path,
13):
14    # each call to `path_factory` returns a unique path
15    assert path_factory() != path_factory()
16
17    # each call to `path_factory` returns a path that is a child of `tmp_path`
18    assert path_factory().parent == path_factory().parent == tmp_path
19
20    with tmp_env() as prefix:
21        out, err, code = conda_cli("rename", "--prefix", prefix, path_factory())
22
23        assert out
24        assert not err  # no error
25        assert not code  # success!

使用固定装置的测试#

有时在集成测试中,你可能希望多次重复使用相同类型的环境。将此设置和拆卸代码复制粘贴到每个单独的测试中会使这些测试更难阅读,也更难维护。

为了克服这个问题,conda 测试广泛使用 pytest 固定装置。下面是之前显示的测试示例,只是我们现在将测试的重点放在 conda env list 命令上,并将环境的创建和删除移到一个固定装置中

针对 conda create 的集成测试#
 1import json
 2from pathlib import Path
 3
 4from conda.testing import CondaCLIFixture
 5
 6
 7@pytest.fixture
 8def env_one(tmp_env: TmpEnvFixture) -> Path:
 9    with tmp_env() as prefix:
10        yield prefix
11
12
13def test_conda_create(env_one: Path, conda_cli: CondaCLIFixture):
14    # verify everything worked using the `conda env list` command
15    out, err, code = conda_cli("env", "list", "--json")
16
17    assert any(
18        env_one.samefile(path)
19        for path in json.loads(out).get("envs", [])
20    )
21    assert not err  # no errors
22    assert not code  # success!

在名为 env_one 的固定装置中,我们使用 tmp_env 固定装置创建一个新环境。我们使用 `yield` 来标记设置的结束。由于 tmp_env 固定装置扩展了 tmp_path,因此不需要额外的拆卸。

此固定装置将使用 pytest 中的默认范围运行,该范围是 function。这意味着设置和拆卸将在请求此固定装置的每个测试之前和之后发生。如果您需要在测试之间共享环境或其他数据,请记住适当地设置固定装置范围。在此处阅读有关 pytest 固定装置范围的更多信息。