Web 测试辅助工具¶
概述¶
在 “@odoo/hoot” 模块之后,测试文件中最常使用的第二个模块应该是 "@web/../tests/web_test_helpers"。
该模块包含所有辅助工具,它们将 Hoot 提供的底层辅助工具与 Odoo 测试中最常用的功能结合起来。
这类辅助工具数量众多,本节文档只重点介绍其中最常用的部分,以及它们彼此交互的方式。
如需查看可用辅助工具的完整列表,请参阅 web_test_helpers 文件。
模拟环境¶
makeMockEnv 辅助工具是能创建 env 的最底层辅助工具。
它会负责以下事项:
创建
env对象本身,并预先配置好 Web 组件正常运行所需的所有属性,例如getTemplate或translateFn;如果该测试尚未创建过,则创建
MockServer;启动所有已注册的 服务,并等待它们全部就绪;
初始化其他不依附于服务的功能,例如 Web
router;确保在测试结束时清理其设置中涉及的所有功能。
该方法非常适合测试底层功能,例如不依附于 Component 的 服务:
// 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 辅助工具。
挂载组件¶
通过 mountWithCleanup 辅助工具,实例化 组件 并将其追加到 DOM 中应当是一件轻松的事。它会在内部准备 env,如果尚不存在则创建,进而确保 MockServer 正在运行。
它的第一个参数是一个 Component 类,第二个参数是 可选 的参数,用于指定 props 或自定义的 target:
await mountWithCleanup(Checkbox, {
props: {
value: false
},
});
该辅助工具将返回活动的 Component 实例。
重要
通常 不建议 获取 Component 实例来直接与其交互,或对其内部变量执行断言。唯一“可接受”的用例是 Component 在 DOM 中显示难以获取的信息时,例如 canvas 中的图表。大多数情况下,强烈建议从 DOM 中查询派生信息。
挂载视图¶
挂载视图只需使用 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 组件实例。但在某些情况下仍可这样做,例如 图表视图。
与组件交互¶
Hoot 提供了与 DOM 交互的辅助工具(例如 click、press 等)。然而,在与较复杂的组件交互时,这些辅助工具有 2 个问题:
辅助工具会尝试立即交互,而有时元素尚未被追加到文档中(所需时间也无法确定);
辅助工具对每个派发的事件只等待一个微任务周期,而大多数基于 Owl 的 UI 至少需要一个完整的动画帧才能完成更新。
// 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");
这种方法虽然看似离“单元测试”的概念稍远一些,但仍然是测试更复杂单元的一种不错且便捷的方式,例如 视图、WebClient,或 服务 与组件之间的成对交互。
但它不应成为所有交互的默认做法,因为其中一些交互仍需在给定的时间范围内 精确 发生,而这是 contains 完全忽略的概念。
注解
Hoot 中的大多数辅助工具都可以作为 contains 实例的方法使用,其形式与 API 通常相同。