Odoo 编写 Python代码测试用例
Odoo 编写 Python代码测试用例
odoo.test
模块提供了一组用于编写单元测试和功能测试的工具和类。使用这些工具和类,开发人员可以编写各种类型的测试用例,以验证Odoo模块的正确性和稳定性。
odoo.test
模块的主要特点和功能包括:
-
支持单元测试和功能测试:odoo.test模块允许开发人员编写单元测试和功能测试,以验证模块的各个部分的行为和功能。
-
提供测试基类:odoo.test模块提供了一些测试基类,可以继承并使用其中的方法来编写测试用例。
-
数据库事务管理:odoo.test模块使用数据库事务来管理测试用例的数据,以确保每个测试用例在一个独立的环境中运行。
-
断言方法:odoo.test模块提供了一组断言方法,用于验证测试结果是否符合预期。
-
测试装置:odoo.test模块提供了一些测试装置,用于模拟和控制测试环境中的各种情况和条件。
Odoo.test编写测试用例
详细介绍请参考Testing Odoo官方文档。
目录结构
Odoo项目是由每个模块组成的,测试用例编写在每个模块内,在模块内创建tests
文件夹,每个测试用例以test_*.py
方式命名,创建测试用例结构如下:
your_module
├── ...
├── tests
| ├── __init__.py
| ├── test_bar.py
| └── test_foo.py
__init__.py
添加内容:
from . import test_foo, test_bar
主要测试基类和工具类
odoo.test.common
中有一些常用的测试基类和工具类,用于编写测试用例。
-
TransactionCase
:TransactionCase
是一个测试基类,用于编写功能测试用例。- 它继承自
unittest.TestCase
类,并添加了Odoo框架的特定功能。 TransactionCase
类使用数据库事务来管理测试用例的数据,以确保每个测试用例在一个独立的环境中运行。
-
SavepointCase
:SavepointCase
是TransactionCase
的子类,提供了更高级的数据库事务管理功能。- 它在每个测试用例之前创建一个保存点,并在测试用例运行结束后回滚到该保存点,以确保测试用例之间的数据隔离性。
-
HttpCase
:HttpCase
是一个测试基类,用于编写HTTP请求的功能测试用例。- 它继承自
TransactionCase
类,并添加了模拟HTTP请求和处理的功能。
-
SingleTransactionCase
:SingleTransactionCase
是一个测试基类,用于编写单个数据库事务中的测试用例。- 它继承自
TransactionCase
类,并在每个测试用例之前创建一个数据库事务,并在测试用例运行结束后回滚该事务。
SavepointCase类
SavepointCase
使用数据库事务来管理测试用例的数据,但它使用了更高级的保存点(savepoint
)机制。 在每个测试用例运行之前,SavepointCase
会创建一个保存点,并在测试用例运行结束后回滚到该保存点。这样,测试用例之间可以共享一部分数据状态,而不需要完全重置数据。SavepointCase
适用于测试用例之间需要共享一些数据状态的情况,以减少数据重置的开销。
所以在编写测试用历史更推荐使用SavepointCase
作为一个模块的各个测试用例的数据初始化。
在tests
文件夹下创建一个common.py
文件。
from odoo.tests.common import SavepointCase
from odoo.tests import tagged
# The CI will run these tests after all the modules are installed,
# not right after installing the one defining it.
@tagged('at_install')
class YourModelTestCase(SavepointCase):
@classmethod
def setUpClass(cls):
# add env on cls and many other things
super(YourModelTestCase, cls).setUpClass()
# create the data for each tests. By doing it in the setUpClass instead
# of in a setUp or in each test case, we reduce the testing time and
# the duplication of code.
# 只会在整个测试类中的第一个测试方法执行前调用一次。
# 创建测试用户
user_model = 'res.users'
cls.your_model= 'name.your_model'
cls.exclude_user = cls.env[user_model].search([('login', '=', 'admin')], limit=1)
cls.other_user = cls.env[user_model].search([('login', '!=', 'admin')], limit=1)
exclude_records = [
{'exclude_type': 'User', 'user_id': cls.exclude_user.id}
]
cls.env[cls.your_model].create(exclude_records)
from odoo.tests import tagged
简介:
官方链接: invocation
@tagged
装饰器可以用于特定的测试场景。以下是一些常用的内置测试标签:
at_install
:表示测试用例在安装模块时运行。post_install
:表示测试用例在安装模块后运行。standard
:表示标准测试用例,通常在模块的tests
目录中找到。
也可以在测试用例中使用多个标签,以便更好地组织和选择性地运行测试。
命令行中使用方法:[-][tag][/module][:class][.method]
同时也可以用@tagged
装饰器在class上自定义标签,通过自定的标签来执行要测试(或要排除)的测试用例。
编写测试用例
在tests
目录下创建测试用例文件test_model_exclude_setting.py
,TestYourModelExcludeSetting
类集成我们刚才创建的common.YourModelTestCase
类,就可以使用YourModelTestCase
中初始化的测试数据。
当此测试用例需要其他的测试数据,可以在setUp(self)
中初始化。
每个单独的测试方法建议使用test_01_xxx
方式创建,因为unitest是根据方法名称的排序执行的,所以test后面跟数字命名来保证测试方法的执行顺序。
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import common
class TestYourModelExcludeSetting(common.YourModelTestCase):
def setUp(self):
# 初始化数据准备
# 每个测试方法都会在执行前都会调用一次 setUp() 方法
return super(TestYourModelExcludeSetting, self).setUp()
def get_record_counts(self, domain):
return self.env[self.your_model].get_records_count(domain)
def test_01_get_record_counts(self):
'''
验证get_record_counts方法返回记录是否正确
'''
# 根据创建排除的测试用户获取记录条数
exclude_user_record_count = self.get_record_counts(
[('exclude_type', '=', 'User'), ('user_id', '=', self.exclude_user.id)]
)
self.assertTrue(exclude_user_record_count > 0,
f'Can not find exclude user: {self.exclude_user.name}')
def test_02_menu_check_exclude(self):
'''
check_exclude方法验证输入菜单是否被屏蔽
'''
...
断言
断言是测试用例中判断测试结果是否成功的关键,下面是odoo.tests中一些断言的使用方法介绍。
在Odoo的测试中,我们可以使用多种断言方法来验证预期结果和实际结果是否匹配。以下是一些常用的断言方法及其说明:
-
assertEqual(a, b, msg=None)
:验证a和b是否相等。如果不相等,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertNotEqual(a, b, msg=None)
:验证a和b是否不相等。如果相等,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertTrue(x, msg=None)
:验证x是否为True。如果x为False,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertFalse(x, msg=None)
:验证x是否为False。如果x为True,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertIs(a, b, msg=None)
:验证a和b是否是同一个对象。如果不是同一个对象,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertIsNot(a, b, msg=None)
:验证a和b是否不是同一个对象。如果是同一个对象,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertIsNone(x, msg=None)
:验证x是否为None。如果x不为None,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertIsNotNone(x, msg=None)
:验证x是否不为None。如果x为None,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertIn(a, b, msg=None)
:验证a是否在b中。如果a不在b中,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。 -
assertNotIn(a, b, msg=None)
:验证a是否不在b中。如果a在b中,将引发AssertionError异常。可以使用msg
参数来自定义错误消息。
执行测试用例
coverage参数说明:
coverage是一个用于测试代码覆盖率的Python库。它可以帮助你了解你的测试用例是否覆盖了源代码的各个部分。下面是一些常用的coverage命令参数的说明:
run
:运行测试用例并收集代码覆盖率数据。--source=<source>
:指定要收集覆盖率数据的源代码目录或模块。--omit=<patterns>
:指定要忽略的文件或目录的模式。可以使用通配符来匹配多个文件或目录。--include=<patterns>
:指定要包含在覆盖率报告中的文件或目录的模式。同样可以使用通配符来匹配多个文件或目录。report
:生成代码覆盖率报告。html
:生成HTML格式的代码覆盖率报告。xml
:生成XML格式的代码覆盖率报告。
以下是一个示例命令,用于运行测试用例并生成HTML格式的代码覆盖率报告:
coverage run --source=my_module -m unittest discover
coverage html
这将运行名为"my_module"的模块的测试用例,并生成一个名为"htmlcov"的文件夹,其中包含了HTML格式的代码覆盖率报告。
请注意,以上只是coverage的一些常用参数说明,可以通过运行 coverage --help
命令来查看更多参数选项和详细说明。
生成测试报告
使用coverage检测测试代码覆盖率并执行测试用例,安装coverage:pip install coverage
。
coverage run --source='project\addons\your_model' odoo\odoo-bin -c project_dev.conf --test-tags=your_model -u your_model
查看代码覆盖率
coverage report -m
Name Stmts Miss Cover Missing
----------------------------------------------------------------------------------------------------------------
project\addons\your_model\__init__.py 2 0 100%
project\addons\your_model\__manifest__.py 1 1 0% 2
project\addons\your_model\controllers\__init__.py 1 0 100%
project\addons\your_model\controllers\controllers.py 0 0 100%
project\addons\your_model\models\__init__.py 1 0 100%
project\addons\your_model\models\models.py 98 27 72% 27-40, 45-47, 67-77, 143
project\addons\your_model\tests\__init__.py 1 0 100%
project\addons\your_model\tests\common.py 18 0 100%
project\addons\your_model\test_model_exclude_setting.py 50 0 100%
----------------------------------------------------------------------------------------------------------------
TOTAL 172 28 84%
生成测试报告coverage html
命令默认会在当前目录下生成一个名为"htmlcov"的文件夹,其中包含了HTML格式的代码覆盖率报告。
如果你希望将生成的报告放在指定的位置,可以使用 --directory 参数指定目标目录。以下是示例命令
coverage html --directory=/path/to/target/directory
总结
本章很详细的介绍了Odoo项目中测试用例的编写方法,希望大家多进行尝试和阅读官方文档。在实际编写测试用例之前也是认为并没有多大的用处,但是实际执行时就会暴露我们想不到的问题,可以提前解决项目中隐藏的问题,所以希望我们尽量多写测试用例,同时结合coverage
命令查询测试用例覆盖率,尽可能覆盖我们的业务逻辑,在问题发生之前提前解决隐患。
在上述测试用例中我们只使用了SavepointCase
基类,也是使用最多的一个,还有常用的TransactionCase
和SingleTransactionCase
后面也会介绍。这些基类最大的不同是在于事务的范围不一样,大家可以根据官方文档和上面说明自己编写测试用例进行验证。