@xstate/test
@xstate/test 包 包含了用于促进任何软件的基于模型的测试的工具。
观看演讲:写更少的测试!从自动化到自动生成 在 React Rally 2019 (🎥 视频)
快速开始
- 安装
xstate
和@xstate/test
:
npm install xstate @xstate/test
- 创建将用于建模被测系统(SUT)的状态机:
import { createMachine } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: 'active',
},
},
active: {
on: {
TOGGLE: 'inactive',
},
},
},
});
- 为状态机中的每个状态添加断言(在此示例中,使用 Puppeteer):
// ...
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: {
/* ... */
},
meta: {
test: async (page) => {
await page.waitFor('input:checked');
},
},
},
active: {
on: {
/* ... */
},
meta: {
test: async (page) => {
await page.waitFor('input:not(:checked)');
},
},
},
},
});
- 创建模型:
import { createMachine } from 'xstate';
import { createModel } from '@xstate/test';
const toggleMachine = createMachine(/* ... */);
const toggleModel = createModel(toggleMachine).withEvents({
TOGGLE: {
exec: async (page) => {
await page.click('input');
},
},
});
- 创建测试计划并运行覆盖率测试 :
// ...
describe('toggle', () => {
const testPlans = toggleModel.getShortestPathPlans();
testPlans.forEach((plan) => {
describe(plan.description, () => {
plan.paths.forEach((path) => {
it(path.description, async () => {
// 进行任何设置,然后...
await path.test(page);
});
});
});
});
it('should have full coverage', () => {
return toggleModel.testCoverage();
});
});
API
createModel(machine, options?)
基于传入的 machine
创建一个抽象的测试模型。
参数 | 类型 | 描述 |
---|---|---|
machine | StateMachine | 用于创建抽象模型的状态机。 |
options? | TestModelOptions | 自定义抽象模型的选项 |
返回值
一个 TestModel
实例。
方法
model.withEvents(eventsMap)
为每个事件提供测试细节。eventsMap
中的每个键都是一个对象,其键是事件类型,属性描述了每个事件的执行和测试用例:
exec
(函数): 执行事件的函数。它有两个参数:testContext
(任意类型): 任何上下文测试数据event
(EventObject): 由测试模型发送的事件
cases?
(EventObject[]): 该事件类型可以由测试模型发送的示例事件对象。
示例:
const toggleModel = createModel(toggleMachine).withEvents({
TOGGLE: {
exec: async (page) => {
await page.click('input');
},
},
});
testModel.getShortestPathPlans(options?)
返回基于从测试模型的初始状态到每个其他可达状态的最短路径的测试计划数组。
选项
参数 | 类型 | 描述 |
---|---|---|
filter | function | 接受 state 并返回 true 如果状态应该被遍历,或者返回 false 如果遍历应该停止。 |
这对于防止无限遍历和堆栈溢出错误非常有用:
const todosModel = createModel(todosMachine).withEvents({
/* ... */
});
const plans = todosModel.getShortestPathPlans({
// 告诉算法将状态/事件邻接图限制为拥有少于 5 个待办事项的状态
filter: (state) => state.context.todos.length < 5,
});
testModel.getSimplePathPlans(options?)
返回基于从测试模型的初始状态到每个其他可达状态的简单路径的测试计划数组。
选项
参数 | 类型 | 描述 |
---|---|---|
filter | function | 接受 state 并返回 true 如果状态应该被遍历,或者返回 false 如果遍历应该停止。 |
testModel.getPlanFromEvents(events, options)
参数 | 类型 | 描述 |
---|---|---|
events | EventObject[] | 用于创建计划的事件序列 |
options | { target: string } | 一个包含 target 属性的对象,该属性应匹配事件的目标状态 |
返回一个包含单个路径的单个测试计划的数组,该路径由 events
生成。
如果最后进入的状态与 options.target
不匹配,则抛出错误。
testModel.testCoverage(options?)
测试在执行的测试中是否覆 盖(遍历)了所有状态节点。
选项
参数 | 类型 | 描述 |
---|---|---|
filter | function | 接受每个 stateNode 并返回 true 如果该状态节点应该被覆盖。 |
// 仅测试定义了 `.meta` 属性的状态节点的覆盖率:
testModel.testCoverage({
filter: (stateNode) => !!stateNode.meta,
});
testPlan.description
测试计划的字符串描述,描述了达到 testPlan.state
的目标。
testPlan.paths
从测试模型的初始状态到每个其他可达状态的测试路径。
testPath.description
测试路径的字符串描述,描述了一系列将达到 testPath.state
的事件。
testPath.test(testContext)
通过以下步骤执行 testPath.segments
中的每一步:
- 验证 SUT 处于
segment.state
- 执行
segment.event
的事件
最后,验证 SUT 处于目标 testPath.state
。
注意:如果您的模型有嵌套状态,在验证 SUT 处于该嵌套状态时,每个父状态的 meta.test
方法也会被执行。