# @xstate/test
@xstate/test package (opens new window) 包含有助于任何软件使用 基于模型的测试 (opens new window) 的实用程序。
¥The @xstate/test package (opens new window) contains utilities for facilitating model-based testing (opens new window) for any software.
讲话:React Rally 2019 的 编写更少的测试!从自动化到自动生成 (opens new window) (🎥 视频 (opens new window))
¥Talk: Write Fewer Tests! From Automation to Autogeneration (opens new window) at React Rally 2019 (🎥 Video (opens new window))
# 快速开始
¥Quick Start
安装
xstate
和@xstate/test
:¥Install
xstate
and@xstate/test
:
npm install xstate @xstate/test
创建将用于对被测系统 (SUT) 进行建模的机器:
¥Create the machine that will be used to model the system under test (SUT):
import { createMachine } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: 'active'
}
},
active: {
on: {
TOGGLE: 'inactive'
}
}
}
});
为机器中的每个状态添加断言(在本例中,使用 Puppeteer (opens new window)):
¥Add assertions for each state in the machine (in this example, using Puppeteer (opens new window)):
// ...
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)');
}
}
}
}
});
创建模型:
¥Create the model:
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');
}
}
});
创建测试计划并运行覆盖范围的测试:
¥Create test plans and run the tests with coverage:
// ...
describe('toggle', () => {
const testPlans = toggleModel.getShortestPathPlans();
testPlans.forEach((plan) => {
describe(plan.description, () => {
plan.paths.forEach((path) => {
it(path.description, async () => {
// do any setup, then...
await path.test(page);
});
});
});
});
it('should have full coverage', () => {
return toggleModel.testCoverage();
});
});
# API
# createModel(machine, options?)
根据传入的 machine
创建抽象测试模型。
¥Creates an abstract testing model based on the machine
passed in.
争论 | 类型 | 描述 |
---|---|---|
machine | StateMachine | 用于创建抽象模型的机器。 |
options? | TestModelOptions | 自定义抽象模型的选项 |
返回
¥Returns
TestModel
实例。
¥A TestModel
instance.
方法
¥Methods
# model.withEvents(eventsMap)
提供每个事件的测试详细信息。eventsMap
中的每个键都是一个对象,其键是事件类型,属性描述每个事件的执行和测试用例:
¥Provides testing details for each event. Each key in eventsMap
is an object whose keys are event types and properties describe the execution and test cases for each event:
exec
(功能):执行事件的函数。它有两个参数:¥
exec
(function): Function that executes the events. It is given two arguments:testContext
(任意):任何上下文测试数据¥
testContext
(any): any contextual testing dataevent
(事件对象):测试模型发送的事件¥
event
(EventObject): the event sent by the testing model
cases?
(事件对象[]):测试模型可以发送的此事件类型的示例事件对象。¥
cases?
(EventObject[]): the sample event objects for this event type that can be sent by the testing model.
示例
¥Example
const toggleModel = createModel(toggleMachine).withEvents({
TOGGLE: {
exec: async (page) => {
await page.click('input');
}
}
});
# testModel.getShortestPathPlans(options?)
根据从测试模型的初始状态到每个其他可到达状态的最短路径,返回一组测试计划。
¥Returns an array of testing plans based on the shortest paths from the test model's initial state to every other reachable state.
选项
¥Options
争论 | 类型 | 描述 |
---|---|---|
filter | function | 如果应该遍历状态,则接受 state 并返回 true ;如果应该停止遍历,则返回 false 。 |
这对于防止无限遍历和堆栈溢出错误很有用:
¥This is useful for preventing infinite traversals and stack overflow errors:
const todosModel = createModel(todosMachine).withEvents({
/* ... */
});
const plans = todosModel.getShortestPathPlans({
// Tell the algorithm to limit state/event adjacency map to states
// that have less than 5 todos
filter: (state) => state.context.todos.length < 5
});
# testModel.getSimplePathPlans(options?)
基于从测试模型的初始状态到每个其他可到达状态的简单路径,返回一组测试计划。
¥Returns an array of testing plans based on the simple paths from the test model's initial state to every other reachable state.
选项
¥Options
争论 | 类型 | 描述 |
---|---|---|
filter | function | 如果应该遍历状态,则接受 state 并返回 true ;如果应该停止遍历,则返回 false 。 |
# testModel.getPlanFromEvents(events, options)
争论 | 类型 | 描述 |
---|---|---|
events | EventObject[] | 创建计划的事件顺序 |
options | { target: string } | 具有 target 属性的对象,该属性应与事件的目标状态匹配 |
返回一个具有单个测试计划的数组,该测试计划具有从 events
生成的单个路径。
¥Returns an array with a single testing plan with a single path generated from the events
.
如果最后输入的状态与 options.target
不匹配,则会引发错误。
¥Throws an error if the last entered state does not match the options.target
.
# testModel.testCoverage(options?)
测试在执行的测试中是否覆盖(遍历)所有状态节点。
¥Tests that all state nodes were covered (traversed) in the executed tests.
选项
¥***Options***
争论 | 类型 | 描述 |
---|---|---|
filter | function | 如果该状态节点应该被覆盖,则接收每个 stateNode 并返回 true 。 |
// Only test coverage for state nodes with a `.meta` property defined:
testModel.testCoverage({
filter: (stateNode) => !!stateNode.meta
});
# testPlan.description
测试计划的字符串描述,描述达到 testPlan.state
的目标。
¥The string description of the testing plan, describing the goal of reaching the testPlan.state
.
# testPlan.paths
从测试模型的初始状态到每个其他可到达状态的测试路径。
¥The testing paths to get from the test model's initial state to every other reachable state.
# testPath.description
测试路径的字符串描述,描述将到达 testPath.state
的事件序列。
¥The string description of the testing path, describing a sequence of events that will reach the testPath.state
.
# testPath.test(testContext)
通过以下方式执行 testPath.segments
中的每个步骤:
¥Executes each step in testPath.segments
by:
验证 SUT 是否位于
segment.state
¥Verifying that the SUT is in
segment.state
执行
segment.event
的事件¥Executing the event for
segment.event
最后,验证 SUT 是否位于目标 testPath.state
中。
¥And finally, verifying that the SUT is in the target testPath.state
.
注意:如果你的模型具有嵌套状态,则在验证 SUT 是否处于该嵌套状态时,也会执行该嵌套状态的每个父状态的 meta.test
方法。
¥NOTE: If your model has nested states, the meta.test
method for each parent state of that nested state is also executed when verifying that the SUT is in that nested state.