Skip to content

@xstate/test

@xstate/test 包 包含了用于促进任何软件的基于模型的测试的工具。

观看演讲写更少的测试!从自动化到自动生成 在 React Rally 2019 (🎥 视频)

快速开始

  1. 安装 xstate@xstate/test
npm install xstate @xstate/test
  1. 创建将用于建模被测系统(SUT)的状态机:
import { createMachine } from 'xstate';

const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: 'active',
},
},
active: {
on: {
TOGGLE: 'inactive',
},
},
},
});
  1. 为状态机中的每个状态添加断言(在此示例中,使用 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)');
},
},
},
},
});
  1. 创建模型:
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');
},
},
});
  1. 创建测试计划并运行覆盖率测试:
// ...

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 创建一个抽象的测试模型。

参数类型描述
machineStateMachine用于创建抽象模型的状态机。
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?)

返回基于从测试模型的初始状态到每个其他可达状态的最短路径的测试计划数组。

选项

参数类型描述
filterfunction接受 state 并返回 true 如果状态应该被遍历,或者返回 false 如果遍历应该停止。

这对于防止无限遍历和堆栈溢出错误非常有用:

const todosModel = createModel(todosMachine).withEvents({
/* ... */
});

const plans = todosModel.getShortestPathPlans({
// 告诉算法将状态/事件邻接图限制为拥有少于 5 个待办事项的状态
filter: (state) => state.context.todos.length < 5,
});

testModel.getSimplePathPlans(options?)

返回基于从测试模型的初始状态到每个其他可达状态的简单路径的测试计划数组。

选项

参数类型描述
filterfunction接受 state 并返回 true 如果状态应该被遍历,或者返回 false 如果遍历应该停止。

testModel.getPlanFromEvents(events, options)

参数类型描述
eventsEventObject[]用于创建计划的事件序列
options{ target: string }一个包含 target 属性的对象,该属性应匹配事件的目标状态

返回一个包含单个路径的单个测试计划的数组,该路径由 events 生成。

如果最后进入的状态与 options.target 不匹配,则抛出错误。

testModel.testCoverage(options?)

测试在执行的测试中是否覆盖(遍历)了所有状态节点。

选项

参数类型描述
filterfunction接受每个 stateNode 并返回 true 如果该状态节点应该被覆盖。
// 仅测试定义了 `.meta` 属性的状态节点的覆盖率:

testModel.testCoverage({
filter: (stateNode) => !!stateNode.meta,
});

testPlan.description

测试计划的字符串描述,描述了达到 testPlan.state 的目标。

testPlan.paths

从测试模型的初始状态到每个其他可达状态的测试路径。

testPath.description

测试路径的字符串描述,描述了一系列将达到 testPath.state 的事件。

testPath.test(testContext)

通过以下步骤执行 testPath.segments 中的每一步:

  1. 验证 SUT 处于 segment.state
  2. 执行 segment.event 的事件

最后,验证 SUT 处于目标 testPath.state

注意:如果您的模型有嵌套状态,在验证 SUT 处于该嵌套状态时,每个父状态的 meta.test 方法也会被执行。