Web test helpers

概述

继 “@odoo/hoot” 模块之后,测试文件中第二个最常用的模块应为 "@web/../tests/web_test_helpers"。

该模块包含所有辅助函数,这些辅助函数将 Hoot 提供的底层辅助函数与 Odoo 测试中最常用的特性结合起来。

这些辅助函数很多,本节文档仅重点介绍最常用的辅助函数及其相互交互方式。

完整的可用辅助函数列表请参见 web_test_helpers file。

Mock environment

makeMockEnv 是最底层的辅助函数,能够生成一个 env。

It will take care of

  • 创建 env 对象本身,并预配置好 Web 部件正常运行所需的所有属性,如 getTemplate 或 translateFn;

  • 生成一个 MockServer (若该测试尚未有一个);

  • 启动所有已注册的 services,并等待它们全部就绪;

  • 初始化其他不与某个服务绑定的特性,如 Web router (路由器);

  • 保证在测试结束时拆卸其设置中的所有特性。

该方法非常适合测试底层特性,如未与 Component 绑定的 services:

// Can be further configured, but is already packed with all the necessary stuff
const env = await makeMockEnv();

expect(env.isSmall).toBe(false);

注解

与 makeMockServer 类似,一个测试中只能有一个 env 处于活跃状态。无需手动调用 makeMockEnv 来获取当前环境实例,可调用 getMockEnv 辅助函数代替。

Mounting components

借助 mountWithCleanup 辅助函数,实例化 components 并将其追加到 DOM 应当很简单。它会在内部准备好 env (若尚不存在),进而确保有一个 MockServer 在运行。

它接收一个 Component 类作为第一个参数,以及一个 可选 的第二参数用于指定 props 或自定义 target:

await mountWithCleanup(Checkbox, {
    props: {
        value: false
    },
});

该辅助函数将返回处于活跃状态的 Component 实例。

重要

通常 不建议 获取 Component 实例来直接与其交互或对内部变量进行断言。唯一”可接受”的用例是 Component 在 DOM 中显示难以获取的信息时,例如 canvas 中的图表。大多数情况下,更推荐查询 DOM 中的派生信息。

Mounting views

挂载视图只需使用 mountWithCleanup 配合 View 部件和正确的属性即可。

为此,Web 测试辅助函数导出一个 mountView 辅助函数,接收一个参数对象以确定视图 type、resModel 及其他可选属性,如 XML arch:

// Resolves when the view is fully ready
await mountView({
    type: "list",
    resModel: "res.partner",
    arch: /* xml */ `
        <list>
            <field name="display_name" />
        </list>
    `,
});

与 mountView 构建所用的上一批辅助函数一样,它会确保当前测试既有一个 env 也有一个 MockServer 在运行。

注解

与 mountWithCleanup 一样,不 建议获取返回的 View 部件实例。不过像 Graph view 这样的用例是可以的。

与部件交互

Hoot 提供了与 DOM 交互的辅助函数(如 click、press 等)。不过,当与更复杂的部件交互时,这些辅助函数存在 2 个问题:

  1. 辅助函数尝试立即交互,而有时元素尚未被追加到文档中(时间未知);

  2. 辅助函数对每个派发的事件只等待一个微任务(micro-task)节拍,而大多数基于 Owl 的 UI 至少需要一个完整的动画帧(animation frame)才能更新。

// Edit record name
await click(".o_field_widget[name=name]");
await edit("Gaston Lagaffe");

// Potential error 1: button may not be in the DOM yet
await click(".btn:contains(Save)");

// Potential error 2: view is not yet updated
expect(".o_field_widget[name=name]").toHaveText("Gaston Lagaffe");

基于这些约束,Web 测试辅助函数提供了 contains 辅助函数:

// Combines 'click' + 'edit' + 'animationFrame' calls
await contains(".o_field_widget[name=name]").edit("Gaston Lagaffe");
// Waits for (at least) a full animation frame after the click
await contains(".btn:contains(Save)").click();
expect(".o_field_widget[name=name]").toHaveText("Gaston Lagaffe");

这种方式虽然看起来略偏离”单元测试”的概念,但仍然是测试更复杂单元(如 views、WebClient)或成对的 services 与部件之间交互的一种简洁便捷的方法。

不过它不应成为所有交互的默认做法,因为有些交互仍需在给定的时间框架内 精确 发生,而 contains 完全忽略了这一概念。

注解

Hoot 中的大多数辅助函数也可作为 contains 实例的方法使用,其(通常)形态和 API 相同。