# 测试状态机

一般来说,测试状态机和状态图应该通过测试状态机的 整体行为 来完成; 那是:

给定当前状态,当某些事件序列发生时,被测系统应该处于某种状态和/或表现出特定的输出

这遵循 行为驱动开发 (BDD) (opens new window)黑盒测试 (opens new window) 策略。 不应直接测试状态机的内部工作; 相反,应该测试观察到的行为。 这使得测试状态机比单元测试更接近于集成或端到端 (E2E) 测试。

# 测试纯逻辑

如果你不想测试副作用,例如执行操作或调用 演员,而是想测试纯逻辑,则可以使用 machine.transition(...) 函数来断言已达到特定状态 给定初始状态和事件:

import { lightMachine } from '../path/to/lightMachine';

it('should reach "yellow" given "green" when the "TIMER" event occurs', () => {
  const expectedValue = 'yellow'; // 预期状态值

  const actualState = lightMachine.transition('green', { type: 'TIMER' });

  expect(actualState.matches(expectedValue)).toBeTruthy();
});

# 测试服务

服务的行为和输出可以通过断言它 最终 达到预期状态来测试,给定初始状态和一系列事件:

import { fetchMachine } from '../path/to/fetchMachine';

it('should eventually reach "success"', (done) => {
  const fetchService = interpret(fetchMachine).onTransition((state) => {
    // 这是你期望最终达到状态的地方
    if (state.matches('success')) {
      done();
    }
  });

  fetchService.start();

  // 向服务发送零个或多个事件,使其最终达到预期状态
  fetchService.send({ type: 'FETCH', id: 42 });
});

提示

请记住,大多数测试框架都有一个默认超时,并且异步测试预计会在该超时之前完成。 如有必要,配置超时(例如,jest.setTimeout(timeout) (opens new window))用于更长时间运行的测试。

# 模拟副作用

由于动作和调用/生成 演员 是副作用,因此在测试环境中执行它们可能是不可取的。 你可以使用 machine.withConfig(...) 选项来更改某些操作的实现细节:

import { fetchMachine } from '../path/to/fetchMachine';

it('should eventually reach "success"', (done) => {
  let userAlerted = false;

  const mockFetchMachine = fetchMachine.withConfig({
    services: {
      fetchFromAPI: (_, event) =>
        new Promise((resolve) => {
          setTimeout(() => {
            resolve({ id: event.id });
          }, 50);
        })
    },
    actions: {
      alertUser: () => {
        // 设置一个标志而不是执行原来的动作
        userAlerted = true;
      }
    }
  });

  const fetchService = interpret(mockFetchMachine).onTransition((state) => {
    if (state.matches('success')) {
      // 断言效果已执行
      expect(userAlerted).toBeTruthy();
      done();
    }
  });

  fetchService.start();

  fetchService.send({ type: 'FETCH', id: 42 });
});