向集合添加单元测试
本节描述了向集合添加单元测试以及如何使用ansible-test命令在本地运行这些测试所需的所有步骤。
更多详情,请参见Ansible 模块的单元测试。
理解单元测试的目的
单元测试确保代码段(称为unit)满足其设计要求并按预期工作。有些集合没有单元测试,但这并不意味着不需要单元测试。
unit是模块或插件中使用的类函数或方法。单元测试验证具有特定输入的函数是否返回预期的输出。
单元测试还应验证函数何时引发或处理异常。
Ansible 使用pytest作为测试框架。
完整详情,请参见Ansible 模块的单元测试。
包含在 Ansible 包中需要集成和/或单元测试 你应该为你的集合以及各个模块和插件编写测试,以提高代码的可靠性。要了解如何开始进行集成测试,请参阅向集合添加集成测试。
请参见准备你的环境 来准备你的环境。
确定是否存在单元测试
Ansible 集合单元测试位于tests/units目录中。
单元测试的结构与代码库的结构相匹配,因此测试可以位于tests/units/plugins/modules/和tests/units/plugins/module_utils目录中。如果模块按模块分组组织,则可能存在子目录。
例如,如果你正在为my_module添加单元测试,请检查测试是否已存在于集合源代码树中,路径为tests/units/plugins/modules/test_my_module.py。
单元测试示例
假设以下函数位于my_module中
def convert_to_supported(val):
    """Convert unsupported types to appropriate."""
    if isinstance(val, decimal.Decimal):
        return float(val)
    if isinstance(val, datetime.timedelta):
        return str(val)
    if val == 42:
        raise ValueError("This number is just too cool for us ;)")
    return val
此函数的单元测试至少应检查以下内容:
- 如果函数获得 - Decimal参数,则返回相应的- float值。
- 如果函数获得 - timedelta参数,则返回相应的- str值。
- 如果函数获得 - 42作为参数,则引发- ValueError。
- 如果函数获得任何其他类型的参数,则不执行任何操作并返回相同的值。 
要在名为community.mycollection的集合中编写这些单元测试
- 如果你已经准备好了本地环境,请转到集合根目录。 
cd ~/ansible_collection/community/mycollection
- 为 - my_module创建一个测试文件。如果路径不存在,请创建它。- touch tests/units/plugins/modules/test_my_module.py
- 将以下代码添加到文件中 
# -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function) __metaclass__ = type from datetime import timedelta from decimal import Decimal import pytest from ansible_collections.community.mycollection.plugins.modules.my_module import ( convert_to_supported, ) # We use the @pytest.mark.parametrize decorator to parametrize the function # https://pytest.cn/en/latest/how-to/parametrize.html # Simply put, the first element of each tuple will be passed to # the test_convert_to_supported function as the test_input argument # and the second element of each tuple will be passed as # the expected argument. # In the function's body, we use the assert statement to check # if the convert_to_supported function given the test_input, # returns what we expect. @pytest.mark.parametrize('test_input, expected', [ (timedelta(0, 43200), '12:00:00'), (Decimal('1.01'), 1.01), ('string', 'string'), (None, None), (1, 1), ]) def test_convert_to_supported(test_input, expected): assert convert_to_supported(test_input) == expected def test_convert_to_supported_exception(): with pytest.raises(ValueError, match=r"too cool"): convert_to_supported(42)有关如何模拟
AnsibleModule对象、修补方法(module.fail_json,module.exit_json)、模拟 API 响应等的示例,请参阅Ansible 模块的单元测试。
- 使用 docker 运行测试 
ansible-test units tests/unit/plugins/modules/test_my_module.py --docker
关于覆盖率的建议
使用以下技巧来组织代码和测试覆盖率:
- 使你的函数简单。执行一项操作且副作用很少或没有副作用的小型函数更容易测试。 
- 测试函数的所有可能行为,包括与异常相关的行为,例如引发、捕获和处理异常。 
- 当函数调用 - module.fail_json方法时,还应检查传递的消息。
另请参阅
- Ansible 模块的单元测试
- Ansible 模块的单元测试 
- 测试 Ansible
- Ansible 测试指南 
- 向集合添加集成测试
- 集合的集成测试 
- 集成测试
- 集成测试指南 
- 测试集合
- 测试集合 
- 资源模块集成测试
- 资源模块集成测试 
- 如何测试集合 PR
- 如何在本地测试 pull request 
