Dojo 如何测试 widget
測試
dojo/framework/src/testing/README.mdcommit 84e254725f41d60f624ab5ad38fe82e15b6348a2
用于測試和斷言 Dojo 部件期望的虛擬 DOM 和行為的簡單 API。
-
測試
- Features
-
harness
- API
- Custom Comparators
- selectors
-
harness.expect
-
harness.expectPartial
- harness.trigger
- harness.getRender
-
- Assertion Templates
Features
- 簡單、熟悉和少量的 API
- 重點測試 Dojo 虛擬 DOM 結構
- 默認不需要 DOM
- 全面支持函數式編程和 tsx 聲明
harness
當使用 @dojo/framework/testing 時,harness 是最重要的 API,主要用于設置每一個測試并提供一個執行虛擬 DOM 斷言和交互的上下文。目的在于當更新 properties 或 children 和失效部件時,鏡像部件的核心行為,并且不需要任何特殊或自定義邏輯。
API
harness(renderFunction: () => WNode, customComparators?: CustomComparator[]): Harness;- renderFunction: 返回被測部件 WNode 的函數
- customComparators: 一組自定義比較器的描述符。每個描述符提供一個比較器函數,用于比較通過 selector 和 property 定位的 properties
harness 函數返回的 Harness 對象提供了與被測部件交互的精簡 API:
Harness
- expect: 對被測部件完整的渲染結果執行斷言
- expectPartial: 對被測部件的一部分渲染結果執行斷言
- trigger: 用于在被測部件的節點上觸發函數
- getRender: 根據提供的索引返回對應的渲染器
使用 @dojo/framework/widget-core 中的 w() 函數設置一個待測試的部件簡單且眼熟:
class MyWidget extends WidgetBase<{ foo: string }> {protected render() {const { foo } = this.properties;return v('div', { foo }, this.children);} }const h = harness(() => w(MyWidget, { foo: 'bar' }, ['child']));如下所示,harness 函數也支持 tsx 語法。README 文檔中其余示例使用編程式的 w() API,在 unit tests 中可查看更多 tsx 示例。
const h = harness(() => <MyWidget foo="bar">child</MyWidget>);renderFunction 是延遲執行的,所以可在斷言之間包含額外的邏輯來操作部件的 properties 和 children。
let foo = 'bar';const h = harness(() => {return w(MyWidget, { foo }, [ 'child' ])); };h.expect(/** assertion that includes bar **/); // update the property that is passed to the widget foo = 'foo'; h.expect(/** assertion that includes foo **/)Custom Comparators
在某些情況下,我們在測試期間無法得知屬性的確切值,所以需要使用自定義比較描述符。
描述符中有一個定位要檢查的虛擬節點的 selector,一個應用自定義比較的屬性名和一個接收實際值并返回一個 boolean 類型斷言結果的比較器函數。
const compareId = {selector: '*', // all nodesproperty: 'id',comparator: (value: any) => typeof value === 'string' // checks the property value is a string };const h = harness(() => w(MyWidget, {}), [compareId]);對于所有的斷言,返回的 harness API 將只對 id 屬性使用 comparator 進行測試,而不是標準的相等測試。
selectors
harness API 支持 CSS style 選擇器概念,來定位要斷言和操作的虛擬 DOM 中的節點。查看 支持的選擇器的完整列表 以了解更多信息。
除了標準 API 之外還提供:
- 支持將定位節點 key 屬性簡寫為 @ 符號
- 當使用標準的 . 來定位樣式類時,使用 classes 屬性而不是 class 屬性
harness.expect
測試中最常見的需求是斷言部件的 render 函數的輸出結構。expect 接收一個返回被測部件期望的渲染結果的函數作為參數。
API
expect(expectedRenderFunction: () => DNode | DNode[], actualRenderFunction?: () => DNode | DNode[]);- expectedRenderFunction: 返回查詢節點期望的 DNode 結構的函數
- actualRenderFunction: 一個可選函數,返回被斷言的實際 DNode 結構
expect 也可以接收第二個可選參數,返回要斷言的渲染結果的函數。
h.expect(() => v('div', { key: 'foo' }), () => v('div', { key: 'foo' }));如果實際的渲染輸出和期望的渲染輸出不同,就會拋出一個異常,并使用結構化的可視方法,用 (A) (實際值)和 (E) (期望值)指出所有不同點。
斷言的錯誤輸出示例:
v("div", {"classes": ["root", (A) "other" (E) "another"],"onclick": "function" }, [v("span", {"classes": "span","id": "random-id","key": "label","onclick": "function","style": "width: 100px"}, ["hello 0"])w(ChildWidget, {"id": "random-id","key": "widget"})w("registry-item", {"id": true,"key": "registry"}) ])harness.expectPartial
expectPartial 根據 selector 斷言部件的部分渲染輸出。
API
expectPartial(selector: string, expectedRenderFunction: () => DNode | DNode[]);- selector: 用于查找目標節點的選擇器
- expectedRenderFunction: 返回查詢節點期望的 DNode 結構的函數
- actualRenderFunction: 一個可選函數,返回被斷言的實際 DNode 結構
示例:
h.expectPartial('@child-widget', () => w(Widget, { key: 'child-widget' }));harness.trigger
harness.trigger() 在 selector 定位的節點上調用 name 指定的函數。
interface FunctionalSelector {(node: VNode | WNode): undefined | Function; }trigger(selector: string, functionSelector: string | FunctionalSelector, ...args: any[]): any;- selector: 用于查找目標節點的選擇器
- functionSelector: 要么是從節點的屬性中找到的被調用的函數名,或者是從節點的屬性中返回一個函數的函數選擇器
- args: 為定位到的函數傳入的參數
如果有返回結果,則返回的是被觸發函數的結果。
用法示例:
// calls the `onclick` function on the first node with a key of `foo` h.trigger('@foo', 'onclick'); // calls the `customFunction` function on the first node with a key of `bar` with an argument of `100` // and receives the result of the triggered function const result = h.trigger('@bar', 'customFunction', 100);functionalSelector 返回部件屬性中的函數。函數也會被觸發,與使用普通字符串 functionSelector 的方式相同。
用法示例:
假定有如下 VDOM 樹結構,
v(Toolbar, {key: 'toolbar',buttons: [{icon: 'save',onClick: () => this._onSave()},{icon: 'cancel',onClick: () => this._onCancel()}] });并且你想觸發 save 按鈕的 onClick 函數。
h.trigger('@buttons', (renderResult: DNode<Toolbar>) => {return renderResult.properties.buttons[0].onClick; });harness.getRender
harness.getRender() 返回索引指定的渲染器,如果沒有提供索引則返回最后一個渲染器。
getRender(index?: number);- index: 要返回的渲染器的索引
用法示例:
// Returns the result of the last render const render = h.getRender(); // Returns the result of the render for the index provided h.getRender(1);Assertion Templates
斷言模板(assertion template)允許你構建一個傳入 h.expect() 的期望渲染函數。斷言模板背后的思想來自經常要斷言整個渲染輸出,并需要修改斷言的某些部分。
要使用斷言模板,首先需要導入模塊:
import assertionTemplate from '@dojo/framework/testing/assertionTemplate';然后,在你的測試中,你可以編寫一個基本斷言,它是部件的默認渲染狀態:
假定有以下部件:
class NumberWidget extends WidgetBase<{ num?: number }> {protected render() {const { num } = this.properties;const message = num === undefined ? 'no number passed' : `the number ${num}`;return v('div', [v('span', [message])]);} }基本斷言如下所示:
const baseAssertion = assertionTemplate(() => {return v('div', [v('span', { '~key': 'message' }, [ 'no number passed' ]);]); });在測試中這樣寫:
it('should render no number passed when no number is passed as a property', () => {const h = harness(() => w(NumberWidget, {}));h.expect(baseAssertion); });現在我們看看,為 NumberWidget 部件傳入 num 屬性后,如何測試數據結果:
it('should render the number when a number is passed as a property', () => {const numberAssertion = baseAssertion.setChildren('~message', ['the number 5']);const h = harness(() => w(NumberWidget, { num: 5 }));h.expect(numberAssertion); });這里,我們使用 baseAssertion 的 setChildren() api,然后我們使用特殊的 ~ 選擇器來定位 key 值為 ~message 的節點。~key 屬性(使用 tsx 的模板中是 assertion-key)是斷言模板的一個特殊屬性,在斷言時會被刪除,因此在匹配渲染結構時不會顯示出來。此功能允許你修飾斷言模板,以便能簡單的選擇節點,而不需要擴展實際的部件渲染函數。一旦我們獲取到 message 節點,我們就可以將其子節點設置為期望的 the number 5,然后在 h.expect 中使用生成的模板。需要注意的是,斷言模板在設置值時總是返回一個新的斷言模板,這可以確保你不會意外修改現有模板(可能導致其他測試失敗),并允許你基于新模板,增量逐層構建出新的模板。
斷言模板具有以下 API:
setChildren(selector: string, children: DNode[], type?: 'prepend' | 'replace' | 'append'): AssertionTemplateResult; setProperty(selector: string, property: string, value: any): AssertionTemplateResult; getChildren(selector: string): DNode[]; getProperty(selector: string, property: string): any;總結
以上是生活随笔為你收集整理的Dojo 如何测试 widget的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac OS 下 NVM 的安装与使用
- 下一篇: shell遍历文件