Внешняя среда
Запросы - это один из ключевых компонентов storyshots, работа с которым бывает не простой и требует особого
внимания для сохранения качества тестирования.
Игнорирование query
storyshots предоставляет методы для отслеживания вызовов функций, а именно журнал. Записывать можно абсолютно
любой метод и тут зачастую возникает путаница. Рассмотрим пример:
const createMockUserRepository = (): UserRepository => {
return {
getUser: async () => createUserStub(),
};
};
UserRepository содержит метод getUser который не выполняет никаких сайд-эффектов в БД, но является
недетерминированным. getUser относится к компоненту запросы - а значит данная
функция не должна проверяться.
Вместо этого:
it('shows user', {
arrange: (externals, { journal }) => ({
...externals,
getUser: journal.asRecordable('getUser', externals.getUser),
}),
});
Делать это:
// Не маркировать метод getUser
it('shows user');
Отслеживание getUser является бессмысленным так как метод не выполняет сайд-эффект.
Cайд-эффект - это не просто результат работы выходящий за пределы функции, в рамках спецификации это ещё и видимые сторонними клиентами данные:
- Для сервера - это команды модифицирующие БД
- Для пользователя - это функции рисующие интерфейс на экране
Такие внешние эффекты и фиксируются storyshots в эталоне.
Взаимодействие с getUser проверяется транзитивно, через отображение компонента в котором используются данные из
метода:
const User: React.FC = () => {
const response = useQuery(userRepository.getUser);
if (response.loading) {
return <Preloader />;
}
return <UserInfo user={response.data} />;
};
У данного правила есть одно очень важное исключение - запросы, такие как getUser, хоть и не выполняют сайд-эффектов,
однако могут реализовывать нетривиальную логику, опирающуюся на то, с какими аргументами был вызван метод.
Рекомендуется фиксировать в журнал взаимодействия с подобного рода запросами.
Нестабильные представления
К сожалению, далеко не всегда существует возможность полного контроля над запросами приложения. Вследствие чего появляется вероятность получения нестабильного эталона.
Примером может служить компонент сторонней библиотеки - нотификация, итоговая позиция которой при появлении не всегда одинаковая и из-за этого страдают снимки экрана.
В данного рода проблемой поможет справиться функция retries:
it('shows read notification', {
// У теста будет три попытки на успешное прохождение
retries: (config) => 3,
});
Данный метод не рекомендуется использовать в общем случае. Следует либо подменить функцию виновника, либо целую библиотеку. Также можно исключить сам тестовый сценарий.
Таймеры
UI интерфейсы полны асинхронных взаимодействий, частью которых являются таймеры.
Рассмотрим следующий пример:
// Показать уведомление
const notification = showMessage('Сообщение прочитано');
// Закрыть через 5 секунд
setTimeout(() => notification.close(), 5_000);
Функция выше, показывает пользователю уведомление, после чего ожидает 5 секунд и закрывает его. При тестировании данного поведения, необходимо помнить о запрете использования запросов в историях.
Вместо этого:
it('shows message to a user', {
act: (actor) => actor.screenshot('Message').wait(5_000).screenshot('Hidden'),
});
Делать это:
it('shows message to a user', {
act: (actor) =>
actor
.screenshot('Message')
// Отправить таймеры в будущее на 5 секунд вперёд
.exec(() => window.clock.tick(5_000))
.screenshot('Hidden'),
});
Функция wait из примера будет ожидать 5 секунд после чего продолжит выполнение теста. Это непозволительно, так как
время выполнения теста - это крайне важная величина. Поэтому во втором примере используются ложные таймеры.
В данном примере используется библиотека @storyshots/web-api-mocks которая
выполняет подмену через сайд-эффекты.
Существует также альтернатива ввиде подмены API через инверсию зависимостей, однако данный метод не является рекомендуемым для таймеров.