第二十九章 测试体系

篇别:第五篇 调试与测试

本章学习目标

  • 编写 TransactionCase / SavepointCase@tagged 策略
  • 了解 QUnit / Hoot前端单测 入口与定位官方样例的方法。
  • 使用 HttpCase路由与会话 级集成测试。
  • 建立 性能基线 意识(查询次数、耗时),与第三十一章衔接。

导读:测试是「可执行的规格」

手工点界面无法 回归 每个版本。Python 测试 覆盖 ORM、约束、规则HttpCase 覆盖 Controller 与 Cookie前端测试 覆盖 OWL 组件逻辑。本章给出 最小可运行模式选型表;细节以 odoo.tests 与官方 Testing 文档为准。


29.1 Python 单元测试

29.1.1 知识要点

  • 基类TransactionCase 每测例 回滚事务SavepointCase 在 savepoint 内更快回滚;HttpCaseHTTP 客户端
  • 标签@tagged('post_install', '-at_install') 控制 安装阶段是否运行CI跳过 at_install 以提速。
  • 环境self.envcls.env.usersetUpClass 中准备 多公司 / 多用户 场景。
  • 断言assertEqualassertRaises;业务上多断言 记录规则with self.assertRaises(AccessError))。

29.1.2 案例

from odoo.tests import TransactionCase, tagged
from odoo.exceptions import ValidationError

@tagged("post_install", "-at_install")
class TestLibrary(TransactionCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.Book = cls.env["library.book"]

    def test_create_book(self):
        b = self.Book.create({"name": "T", "isbn": "9780000000000"})
        self.assertTrue(b.id)

    def test_isbn_unique_constraint(self):
        self.Book.create({"name": "A", "isbn": "9781111111111"})
        with self.assertRaises(ValidationError):
            self.Book.create({"name": "B", "isbn": "9781111111111"})

29.1.3 截图占位

图 29-1 终端 Odoo 测试通过

29.1.4 本节练习

  1. 实操:为 「ISBN 唯一」 约束补 失败用例(见上例)。
  2. 简答at_installpost_install 各适合测什么?

参考答案提示:2. at_install:模块结构、快速语法;post_install:依赖模块数据已存在时的业务逻辑。


29.2 JavaScript 单元测试(补充)

29.2.1 知识要点

  • Odoo 前端测试框架随版本演进(QUnit → 新套件);以 web 模块内测试官方文档 为准。
  • OWL 组件mountfixturemock serviceormnotification),断言 DOM / state
  • 定位样例:在 addons/web/static/tests(路径以实际源码树为准)搜索 mountgetFixture

29.2.2 案例(思路)

  1. 复制 官方最小字段组件测试 结构。
  2. 替换为你的 组件类模板
  3. mock orm.searchRead 返回固定列表,断言渲染文本。

29.2.3 截图占位

图 29-2 前端测试运行器通过

29.2.4 本节练习

  1. 拓展:在源码树中找到 一个 官方 OWL 测试文件路径并记录。
  2. 简答:前端测试 不能 替代哪些 后端测试

参考答案提示:2. ACL、规则、约束、并发写入、SQL 注入防护等。


29.3 集成测试

29.3.1 知识要点

  • HttpCaseself.url_open('/web/login')authenticate;可测 CSRF、路由、重定向
  • 浏览器 E2E:可选 Playwright / Selenium 外挂;维护成本高,宜 少量关键路径
  • 邮件 / 外部 APImock.patch 或 Odoo 邮件测试助手mail 模块工具),避免 真实外发

29.3.2 案例

from odoo.tests import HttpCase, tagged

@tagged("-at_install", "post_install")
class TestLibraryHttp(HttpCase):
    def test_public_page_ok(self):
        self.authenticate("admin", "admin")
        res = self.url_open("/web")
        self.assertEqual(res.status_code, 200)

29.3.3 截图占位

图 29-3 HttpCase 失败时的响应码与 body 片段

29.3.4 本节练习

  1. 简答SavepointCaseTransactionCase 选型?
  2. 实操:写一个 需登录 才能访问的 controller,用 HttpCase 分别测 未登录 403/302已登录 200

参考答案提示:1. SavepointCase:大量相关用例共享昂贵 setUp 时更快;否则 TransactionCase 简单直接。


29.4 性能测试(新增)

29.4.1 知识要点

  • 查询计数:Odoo 提供 测试工具 断言 SQL 次数(API 以当前版本为准,常见为 assertQueryCount 或 profiler 钩子)。
  • 基线:在 CI 中记录 主路径查询次数回退即失败(允许小幅浮动需 团队约定)。
  • 瓶颈N+1 优先用 search_fetch / prefetch 修(见第三十一章)。

29.4.2 案例(概念)

# 伪代码:名称以 odoo.tests 实际 API 为准
# with self.assertQueryCount(...):
#     records.do_something_heavy()

实施前在目标版本 shell 中 help(odoo.tests) 或查 官方 Testing 文档确认 准确装饰器名

29.4.3 截图占位

图 29-4 CI 中 query count 断言失败 diff

29.4.4 本节练习

  1. 实操:选一业务方法,用 日志或 profiler 记录 冷启动 SQL 次数 作为 口头基线
  2. 简答:性能测试为何要 固定随机种子 / 数据量

参考答案提示:2. 否则基线漂移,误报/漏报。


本章综合练习

  1. CI 矩阵Odoo 版本 × Python 版本 最小组合建议(自洽说明一条理由)。
  2. 数据:测试数据用 setUpClass 批量创建 还是 fixture XML?各举 一利一弊
  3. 综合:某用例在 本地绿、CI 红,列出 四条 排查方向(时区、标签、依赖模块、演示数据)。
  4. 安全:测试中 sudo() 使用原则?

参考答案提示:4. 仅用于 搭建数据;断言规则时应用 普通用户 上下文。


本章对应白皮书目录:第二十九章 测试体系。