编写测试#

本节包含一系列关于在 conda 仓库中编写测试的指南和准则。


指南#

集成测试 本指南概述了如何使用完整的命令调用编写集成测试。它还涵盖了创建 fixture 以用于这些类型的测试。


通用准则#

注意

应该注意的是,现有的测试可能偏离这些准则,这没关系。这些准则旨在说明我们希望所有新测试的外观和功能。

首选测试风格 (pytest)#

尽管我们的代码库包含基于类的 unittest 测试,但我们所有新测试的首选格式是 pytest 风格的测试。这些测试使用函数编写,并使用 fixture 处理测试上下文的设置和拆卸。我们建议您先熟悉 pytest,然后再尝试为 conda 编写测试。前往他们的 入门指南 了解更多信息。

组织测试#

测试的组织方式应镜像主 conda 模块。例如,如果您要为 conda/base/context.py 中的函数编写测试,则应将此测试放在 tests/base/test_context.py 中。

“conda.testing”模块#

这是一个模块,其中包含任何可能有助于编写测试的内容,包括 fixture、函数和类。请随意向此模块添加内容,但请注意组织。例如,如果您的测试实用程序主要仅用于 base 模块,请考虑将它们存储在 conda.testing.base 中。

添加新的 fixture#

对于范围或目的非常有限的 fixture,可以与测试本身一起定义它们。但是,如果这些 fixture 可以跨多个测试使用,则应将它们保存在单独的 fixtures.py 文件中。conda.testing 模块已经包含其中几个文件。

如果您想在新文件中添加新的 fixture,请务必在 tests/conftest.py::pytest_plugins 中添加对此模块的引用。这是我们将 fixture 提供给测试的首选方式。由于它们包含在环境中的方式,您应该注意命名方案,并选择不太可能相互冲突的方案。考虑使用前缀来实现此目的。

上下文对象#

conda 中的上下文对象用作单例。这意味着每次 conda 命令运行时,只会实例化一个对象。这是有道理的,因为它保存了程序的所有配置,重新实例化它或制作多个副本将是低效的。

这在测试期间会导致问题,您可能希望在同一进程中运行 conda 命令数百次。因此,在编写测试时,务必始终将此对象重置为全新状态。

这可以通过使用 reset_context 函数来实现,该函数也位于 conda.base.context 模块中。以下示例显示了如何修改上下文对象,然后使用 reset_conda_context pytest fixture 重置它

import os
import tempfile

from conda.base.context import reset_context, context
from conda.testing.fixtures import reset_conda_context

TEST_CONDARC = """
channels:
  - test-channel
"""


def test_that_uses_context(reset_conda_context):
    # We first created a temporary file to hold our test configuration
    with tempfile.TemporaryDirectory() as tempdir:
        condarc_file = os.path.join(tempdir, "condarc")

        with open(condarc_file, "w") as tmp_file:
            tmp_file.write(TEST_CONDARC)

        # We use the reset_context function to load our new configuration
        reset_context(search_path=(condarc_file,))

        # Run various test assertions, below is an example
        assert "test-channel" in context.channels

使用此测试 fixture 确保您的上下文对象返回到测试之前的状态。对于此特定测试,这意味着 channels 设置将返回到其默认配置。如果您需要在测试期间手动重置上下文,您可以通过手动调用 reset_context 命令来完成,如下例所示

from conda.base.context import reset_context


def test_updating_context_manually():
    # Load some custom variables into context here like above...

    reset_context()

    # Continue testing with a fresh context...