Dojo 如何测试 widget
栏目: JavaScript · 发布时间: 5年前
内容简介:用于测试和断言 Dojo 部件期望的虚拟 DOM 和行为的简单 API。当使用harness 函数返回的
测试
commit 84e254725f41d60f624ab5ad38fe82e15b6348a2
用于测试和断言 Dojo 部件期望的虚拟 DOM 和行为的简单 API。
Features
- 简单、熟悉和少量的 API
- 重点测试 Dojo 虚拟 DOM 结构
- 默认不需要 DOM
- 全面支持函数式编程和 tsx 声明
harness
当使用 @dojo/framework/testing
时, harness
是最重要的 API,主要用于设置每一个测试并提供一个执行虚拟 DOM 断言和交互的上下文。目的在于当更新 properties
或 children
和失效部件时,镜像部件的核心行为,并且不需要任何特殊或自定义逻辑。
API
harness(renderFunction: () => WNode, customComparators?: CustomComparator[]): Harness;
-
renderFunction
: 返回被测部件 WNode 的函数 -
: 一组自定义比较器的描述符。每个描述符提供一个比较器函数,用于比较通过
selector
和property
定位的properties
harness 函数返回的 Harness
对象提供了与被测部件交互的精简 API:
Harness
- : 对被测部件完整的渲染结果执行断言
- : 用于在被测部件的节点上触发函数
使用 @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
在某些情况下,我们在测试期间无法得知属性的确切值,所以需要使用自定义比较描述符。
描述符中有一个定位要检查的虚拟节点的,一个应用自定义比较的属性名和一个接收实际值并返回一个 boolean 类型断言结果的比较器函数。
const compareId = { selector: '*', // all nodes property: '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
结构
h.expect(() => v('div', { key: 'foo' }, [w(Widget, { key: 'child-widget' }), 'text node', v('span', { classes: ['class'] })]) );
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
根据断言部件的部分渲染输出。
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;
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Flutter 学习之路 - 测试(单元测试,Widget 测试,集成测试)
- itest(爱测试)接口测试&敏捷测试管理 7.7.7 发布,接口测试重大升级
- 性能测试vs压力测试vs负载测试
- SpringBoot | 第十三章:测试相关(单元测试、性能测试)
- 敏捷测试VS传统测试对比,6招玩转敏捷测试!
- itest(爱测试)接口测试&敏捷测试管理平台 8.1.0 发布
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
松本行弘的程序世界
松本行弘 / 柳德燕、李黎明、夏倩、张文旭 / 人民邮电出版社 / 2011-8 / 75.00元
《松本行弘的程序世界》是探索程序设计思想和方法的经典之作。作者从全局的角度,利用大量的程序示例及图表,深刻阐述了Ruby编程语言的设计理念,并以独特的视角考察了与编程相关的各种技术。阅读《松本行弘的程序世界》不仅可以深入了解编程领域各个要素之间的关系,而且能够学到大师的思考方法。 《松本行弘的程序世界》面向各层次程序设计人员和编程爱好者,也可以供相关技术人员参考。一起来看看 《松本行弘的程序世界》 这本书的介绍吧!