完整目录请见Odoo 14全新前端框架 OWL(Odoo Web Library)官方文档中文版
🦉 如何测试组件 🦉
内容
综述
测试应用及组件来保障其行为和预期一致是一种良好实践。有很多测试用户界面的方式:手动测试、集成测试、单元测试…
本节中,我们将讨论如何为组件编写单元测试。
单元测试
为Owl组件编写单元测试其实是取决于项目中所使用的测试框架。但通常,它包含如下步骤:
- 创建测试文件:例如
SomeComponent.test.js
, - 在该文件中为
SomeComponent
导入代码, - 添加测试用例:
- 创建一个真实的DOM元素用作测试夹具,
- 创建测试环境
- 创建
SomeComponent
的实例,载入到夹具中 - 与组件交互并对某些属性进行断言
为有助于这一操作,包含一些通用工具函数的helper.js
文件会比较有用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
export function makeTestFixture() { let fixture = document.createElement("div"); document.body.appendChild(fixture); return fixture; } export function nextTick() { let requestAnimationFrame = owl.Component.scheduler.requestAnimationFrame; return new Promise(function(resolve) { setTimeout(() => requestAnimationFrame(() => resolve())); }); } export function makeTestEnv() { // 针对具体应用。需要一种载入实际模板的方式 const templates = ...; return { qweb: new QWeb(templates), ..., // each service can be mocked here }; } |
通过这一文件,典型针对Jest的测试套件如下这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// in SomeComponent.test.js import { SomeComponent } from "../../src/ui/SomeComponent"; import { nextTick, makeTestFixture, makeTestEnv} from '../helpers'; //------------------------------------------------------------------------------ // Setup //------------------------------------------------------------------------------ let fixture: HTMLElement; let env: Env; beforeEach(() => { fixture = makeTestFixture(); env = makeTestEnv(); // we set here the default environment for each component created in the test Component.env = env; }); afterEach(() => { fixture.remove(); }); //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ describe("SomeComponent", () => { test("component behaves as expected", async () => { const props = {...}; // depends on the component //const comp = new SomeComponent(null, props); //await comp.mount(fixture); <span class="pl-k"> const</span> <span class="pl-c1">comp</span> <span class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-en">mount</span>(SomeComponent, { target<span class="pl-k">:</span> fixture, props }); // do some assertions expect(...).toBe(...); fixture.querySelector('button').click(); await nextTick(); // some other assertions expect(...).toBe(...); }); }); |
注意Owl不会等待下一个动画帧实际去更新DOM。这也是为什么有必要通过nextTick
(或其它方法)来确保DOM是最新的。
通常等待Owl完全更新组件后是有益的(尤其是在有高度并发用界面时)。下一个帮助方法在内部Owl任务队列并在解析为空返回promise时的每20秒进行触发:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function afterUpdates() { return new Promise((resolve, reject) => { let timer = setTimeout(poll, 20); let counter = 0; function poll() { counter++; if (owl.Component.scheduler.tasks.length) { if (counter > 10) { reject(new Error("timeout")); } else { timer = setTimeout(poll); } } else { resolve(); } } }); } |