单元测试

单元测试是针对特定库或模块的小型隔离测试。Ansible 中的单元测试目前是唯一一种在 Ansible 的持续集成过程中从 Python 驱动测试的方法。这意味着在某些情况下,测试可能会比仅仅是单元测试范围更广。

可用测试

可以在 test/units 中找到单元测试。注意,测试的目录结构与 lib/ansible/ 的目录结构相匹配。

运行测试

注意

要使用 Docker 运行单元测试,请始终使用默认的 Docker 镜像,方法是传递 --docker--docker default 参数。

可以通过以下方式运行整个代码库的 Ansible 单元测试:

cd /path/to/ansible/source
source hacking/env-setup
ansible-test units --docker -v

可以通过以下方式针对单个文件运行测试:

ansible-test units --docker -v apt

或者可以通过以下方式针对特定 Python 版本运行测试:

ansible-test units --docker -v --python 2.7 apt

如果您正在针对模块以外的其他内容(例如模块实用程序)运行单元测试,请指定整个文件路径:

ansible-test units --docker -v test/units/module_utils/basic/test_imports.py

有关高级用法,请参阅在线帮助

ansible-test units --help

您还可以通过打开拉取请求在 Ansible 的持续集成系统中运行测试。这将根据您在拉取请求中所做的更改自动确定要运行的测试。

安装依赖项

如果您使用 --docker--venv 选项运行 ansible-test,则无需手动安装依赖项。

否则,您可以使用 --requirements 选项安装依赖项,这将安装单元测试所需的所有依赖项。例如:

ansible-test units --python 2.7 --requirements apache2_module

可以在 test/units/requirements.txt 中找到单元测试要求的列表。

这并不包括 ansible-test 本身的单元测试要求列表,该列表可以在 test/lib/ansible_test/_data/requirements/units.txt 中找到。

另请参阅适用于所有测试命令的 约束

扩展单元测试

警告

单元测试不是什么

如果您开始编写需要外部服务的测试,那么您可能正在编写集成测试,而不是单元测试。

构建单元测试

Ansible 通过 pytest 驱动单元测试。这意味着测试可以编写为简单的函数,这些函数包含在任何文件名(例如 test_<something>.py)中,也可以编写为类。

以下是一个函数示例:

#this function will be called simply because it is called test_*()

def test_add():
    a = 10
    b = 23
    c = 33
    assert a + b == c

以下是一个类的示例:

import unittest

class AddTester(unittest.TestCase):

    def SetUp():
        self.a = 10
        self.b = 23

    # this function will
    def test_add():
      c = 33
      assert self.a + self.b == c

   # this function will
    def test_subtract():
      c = -13
      assert self.a - self.b == c

这两种方法在大多数情况下都能正常工作;基于函数的接口更简单、更快,因此当您只是尝试为模块添加一些基本测试时,您可能应该从这里开始。基于类的测试允许更整洁地设置和拆除先决条件,因此,如果您的模块有许多测试用例,您可能需要重构以使用它。

使用测试中的简单 assert 函数进行断言将在失败的原因上提供完整的信息,并提供在断言期间调用的函数的跟踪。这意味着建议使用纯断言而不是其他外部断言库。

一些单元测试套件包括在几个模块之间共享的函数,尤其是在网络领域。在这种情况下,将在同一个目录中创建一个文件,然后直接包含该文件。

模块测试用例通用代码

尽可能在 test/units/ 目录结构中保持通用代码的具体性。不要从当前目录或父目录以外的目录导入通用单元测试代码。

不要从单元测试中导入其他单元测试。任何通用代码都应该在专门的文件中,这些文件本身不是测试。

Fixture 文件

要模拟从设备获取结果或提供来自外部库的其他复杂数据结构,可以使用 fixtures 来读取预先生成的 数据。

您可以查看 fixtures 如何在 cpuinfo fact 测试 中使用。

如果您正在模拟 API,您可能会发现 Python placebo 很有用。有关更多信息,请参阅 单元测试 Ansible 模块

新单元测试或更新单元测试的代码覆盖率

新代码将缺少 codecov.io 代码覆盖率报告(请参阅 测试 Ansible),因此需要本地报告。大多数 ansible-test 命令允许您收集代码覆盖率;这在指示扩展测试的位置时特别有用。

要收集覆盖率数据,请将 --coverage 参数添加到您的 ansible-test 命令行中:

ansible-test units --coverage apt
ansible-test coverage html

结果将写入 test/results/reports/coverage/index.html

报告可以以多种不同格式生成:

  • ansible-test coverage report - 控制台报告。

  • ansible-test coverage html - HTML 报告。

  • ansible-test coverage xml - XML 报告。

要清除测试运行之间的数据,请使用 ansible-test coverage erase 命令。有关生成覆盖率报告的更多信息,请参阅 测试 Ansible 和集合

另请参阅

单元测试 Ansible 模块

单元测试模块的特殊注意事项

测试 Ansible 和集合

在本地运行测试,包括收集和报告代码覆盖率数据

Python 3 文档 - 26.4. unittest — 单元测试框架

Python 3 中单元测试框架的文档

Python 2 文档 - 25.3. unittest — 单元测试框架

最早支持的单元测试框架的文档 - 来自 Python 2.6

pytest:帮助您编写更好的程序

pytest 的文档 - 实际上用于运行 Ansible 单元测试的框架