向集合添加单元测试
本节描述了向集合添加单元测试以及如何使用ansible-test
命令在本地运行这些测试所需的所有步骤。
有关更多详细信息,请参阅Ansible 模块的单元测试。
理解单元测试的目的
单元测试确保代码的一部分(称为单元
)满足其设计要求并按预期运行。一些集合没有单元测试,但这并不意味着它们不需要。
一个单元
是模块或插件中使用的类函数或方法。单元测试验证具有特定输入的函数是否返回预期的输出。
单元测试还应验证函数何时引发或处理异常。
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)请参阅Ansible 模块的单元测试,了解如何模拟
AnsibleModule
对象、修补方法(module.fail_json
、module.exit_json
)、模拟 API 响应等等的示例。
使用 docker 运行测试
ansible-test units tests/unit/plugins/modules/test_my_module.py --docker
关于覆盖率的建议
使用以下技巧来组织您的代码和测试覆盖率
使您的函数简单。执行一项操作且没有或最少副作用的小函数更容易测试。
测试函数的所有可能行为,包括与异常相关的行为,例如引发、捕获和处理异常。
当函数调用
module.fail_json
方法时,还应检查传递的消息。
另请参阅
- Ansible 模块的单元测试
Ansible 模块的单元测试
- 测试 Ansible
Ansible 测试指南
- 向集合添加集成测试
集合的集成测试
- 集成测试
集成测试指南
- 测试集合
测试集合
- 资源模块集成测试
资源模块集成测试
- 如何测试集合 PR
如何在本地测试拉取请求