如何在Stenciljs测试中用newE2EPage/newSpecPage触发生命周期方法
在Stencil.js的Spec/E2E测试中触发connectedCallback/disconnectedCallback且避免重复调用componentWillLoad
先准备测试用的组件示例
首先我们需要一个带生命周期计数的组件,方便验证测试逻辑:
import { Component, h } from '@stencil/core'; @Component({ tag: 'my-cmp', }) export class MyCmp { willLoadCount = 0; connectedCount = 0; disconnectedCount = 0; componentWillLoad() { this.willLoadCount++; } connectedCallback() { this.connectedCount++; } disconnectedCallback() { this.disconnectedCount++; } render() { return <div>Test Component</div>; } }
一、使用newSpecPage进行单元测试(Spec测试)
Spec测试运行在JSDOM环境,逻辑和官方原生DOM操作类似,只需通过newSpecPage获取页面上下文:
import { newSpecPage } from '@stencil/core/testing'; import { MyCmp } from './my-cmp'; describe('my-cmp lifecycle tests', () => { it('should trigger connected/disconnected without re-running componentWillLoad', async () => { // 1. 创建Spec页面并挂载组件 const page = await newSpecPage({ components: [MyCmp], html: '<my-cmp></my-cmp>', }); // 获取组件实例 const cmp = page.body.querySelector('my-cmp') as MyCmp; // 验证第一次挂载的计数 expect(cmp.willLoadCount).toBe(1); expect(cmp.connectedCount).toBe(1); expect(cmp.disconnectedCount).toBe(0); // 2. 移除组件,触发disconnectedCallback cmp.remove(); await page.waitForChanges(); // 等待Stencil更新状态 expect(cmp.disconnectedCount).toBe(1); // 3. 重新添加组件到页面,触发connectedCallback但不会触发componentWillLoad page.body.appendChild(cmp); await page.waitForChanges(); // 验证最终计数:willLoad只执行一次,connected执行两次,disconnected执行一次 expect(cmp.willLoadCount).toBe(1); expect(cmp.connectedCount).toBe(2); expect(cmp.disconnectedCount).toBe(1); }); });
二、使用newE2EPage进行端到端测试(E2E测试)
E2E测试运行在真实浏览器环境(基于Playwright),所有DOM操作和属性访问需要通过page.evaluate()在页面上下文内执行:
import { newE2EPage } from '@stencil/core/testing'; describe('my-cmp e2e lifecycle tests', () => { it('should trigger connected/disconnected without re-running componentWillLoad', async () => { const page = await newE2EPage(); // 1. 首次挂载组件 await page.setContent('<my-cmp></my-cmp>'); await page.waitForChanges(); // 获取初始计数 const initialCounts = await page.evaluate(() => { const cmp = document.querySelector('my-cmp'); return { willLoad: cmp.willLoadCount, connected: cmp.connectedCount, disconnected: cmp.disconnectedCount, }; }); expect(initialCounts.willLoad).toBe(1); expect(initialCounts.connected).toBe(1); expect(initialCounts.disconnected).toBe(0); // 2. 移除组件,触发disconnectedCallback await page.evaluate(() => { const cmp = document.querySelector('my-cmp'); cmp.remove(); }); await page.waitForChanges(); const afterRemoveCounts = await page.evaluate(() => { const cmp = document.querySelector('my-cmp'); // 移除后实例仍存在 return { disconnected: cmp.disconnectedCount }; }); expect(afterRemoveCounts.disconnected).toBe(1); // 3. 重新添加组件到页面 await page.evaluate(() => { const cmp = document.querySelector('my-cmp'); document.body.appendChild(cmp); }); await page.waitForChanges(); // 验证最终计数 const finalCounts = await page.evaluate(() => { const cmp = document.querySelector('my-cmp'); return { willLoad: cmp.willLoadCount, connected: cmp.connectedCount, disconnected: cmp.disconnectedCount, }; }); expect(finalCounts.willLoad).toBe(1); // componentWillLoad仅执行一次 expect(finalCounts.connected).toBe(2); // connected执行两次 expect(finalCounts.disconnected).toBe(1); // disconnected执行一次 }); });
关键注意点
- Spec测试中,
await page.waitForChanges()必须调用,确保Stencil完成组件状态更新。 - E2E测试中,测试代码无法直接访问浏览器上下文变量,所有DOM操作和属性读取都要通过
page.evaluate()执行。 - 组件实例在移除后不会被销毁,重新添加时仅触发
connectedCallback,不会再次执行componentWillLoad(该方法仅在组件首次挂载前执行一次)。
内容的提问来源于stack exchange,提问作者Safa Alai
相关产品推荐
相关产品推荐

