Перейти к содержанию

Тестирование автоматов

В общем, тестирование конечных автоматов и диаграмм состояний должно выполняться путем тестирования общего поведения машины; поэтому:

В текущем состоянии, когда происходит некоторая последовательность событий, тестируемая система должна находиться в определенном состоянии или показывать определенные выходные данные.

Такое тестирование следует из поведенческой разработки (BDD) и стратегии тестирования методом черного ящика. Не следует напрямую проверять внутреннюю работу машины; вместо этого следует проверить наблюдаемое поведение. По смыслу, тестирование автоматов ближе к интеграционным тестам, чем к модульным тестам.

Тестирование чистой логики

Если вы не хотите тестировать побочные эффекты, такие как выполнение действий или вызов акторов, и вместо этого хотите протестировать чистую логику, можно использовать функцию machine.transition(...), чтобы убедиться, что определенное состояние достигнуто при заданном исходное состояние и событие:

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

it('should reach "yellow" given "green" when the "TIMER" event occurs', () => {
  const expectedValue = 'yellow'; // the expected state value

  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) => {
      // this is where you expect the state to eventually
      // be reached
      if (state.matches('success')) {
        done();
      }
    }
  );

  fetchService.start();

  // send zero or more events to the service that should
  // cause it to eventually reach its expected state
  fetchService.send({ type: 'FETCH', id: 42 });
});

Подсказка

Имейте в виду, что большинство платформ тестирования имеют тайм-аут по умолчанию, и ожидается, что асинхронные тесты завершатся до этого тайм-аута. При необходимости настройте тайм-аут (например, jest.setTimeout(timeout)) для более длительных тестов.

Подмена (mock) реализации

Поскольку действия и запуск / создание акторов являются побочными эффектами, их выполнение в тестовой среде может быть нежелательным. Вы можете использовать параметр 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: () => {
        // set a flag instead of executing the original action
        userAlerted = true;
      },
    },
  });

  const fetchService = interpret(
    mockFetchMachine
  ).onTransition((state) => {
    if (state.matches('success')) {
      // assert that effects were executed
      expect(userAlerted).toBeTruthy();
      done();
    }
  });

  fetchService.start();

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