Одна из самых распространённых проблем — создание зависимостей между тестами внутри suite. Vest проектировался как система независимых проверок, где каждый тест должен выполняться автономно.
Некорректный пример:
import { create, test } from 'vest';
const suite = create((data) => {
let isPasswordValid = false;
test('password', 'Пароль слишком короткий', () => {
isPasswordValid = data.password.length >= 8;
enforce(isPasswordValid);
});
test('confirmPassword', 'Пароли не совпадают', () => {
enforce(isPasswordValid);
enforce(data.password === data.confirmPassword);
});
});
Проблема заключается в том, что второй тест зависит от результата
первого. Если порядок выполнения изменится или первый тест будет
пропущен через skip, логика сломается.
Правильный вариант:
import { create, test, enforce } from 'vest';
const suite = create((data) => {
test('password', 'Пароль слишком короткий', () => {
enforce(data.password.length >= 8);
});
test('confirmPassword', 'Пароли не совпадают', () => {
enforce(data.password === data.confirmPassword);
});
});
Каждая проверка должна содержать полный набор необходимых условий.
Vest предполагает декларативный стиль валидации. Побочные эффекты
внутри test приводят к трудноуловимым ошибкам.
Плохой подход:
test('email', 'Email занят', async () => {
analytics.track('email_validation');
const result = await api.checkEmail();
enforce(result.available);
});
Проблемы:
Корректнее выносить побочные эффекты за пределы suite.
Очень частая ошибка в React-приложениях — объявление
create() внутри компонента.
Неправильно:
function Form() {
const suite = create((data) => {
test('username', 'Обязательное поле', () => {
enforce(data.username).isNotBlank();
});
});
return null;
}
При каждом рендере создаётся новый экземпляр suite, из-за чего:
Правильная практика:
export const suite = create((data) => {
test('username', 'Обязательное поле', () => {
enforce(data.username).isNotBlank();
});
});
Suite должен создаваться один раз.
Vest поддерживает асинхронные тесты, но ошибки в их использовании встречаются постоянно.
Некорректный пример:
test('email', 'Email уже существует', () => {
fetch('/api/check-email');
});
Vest считает тест синхронным, потому что Promise не возвращается.
Правильно:
test('email', 'Email уже существует', async () => {
const response = await fetch('/api/check-email');
const result = await response.json();
enforce(result.available);
});
Либо:
test('email', 'Email уже существует', () => {
return fetch('/api/check-email')
.then(res => res.json())
.then(result => {
enforce(result.available);
});
});
Иногда разработчики используют test как обычный
callback.
Ошибка:
test('age', 'Возраст некорректен', () => {
data.age > 18;
});
Такой тест всегда считается успешным, потому что отсутствует assertion.
Правильно:
test('age', 'Возраст некорректен', () => {
enforce(data.age > 18);
});
Или:
test('age', 'Возраст некорректен', () => {
enforce(data.age).greaterThan(18);
});
Vest предназначен для проверки данных, а не для выполнения бизнес-операций.
Антипаттерн:
test('payment', 'Ошибка оплаты', async () => {
await paymentService.chargeCard();
});
Валидация не должна:
Корректная задача suite — только определить валидность данных.
Плохо структурированные проверки ухудшают читаемость.
Сложный вариант:
test('password', 'Некорректный пароль', () => {
if (data.password) {
if (data.password.length > 7) {
if (/[A-Z]/.test(data.password)) {
if (/[0-9]/.test(data.password)) {
return;
}
}
}
}
enforce(false);
});
Лучше использовать небольшие независимые проверки:
test('password', 'Пароль обязателен', () => {
enforce(data.password).isNotBlank();
});
test('password', 'Минимум 8 символов', () => {
enforce(data.password.length).greaterThanOrEquals(8);
});
test('password', 'Нужна заглавная буква', () => {
enforce(/[A-Z]/.test(data.password));
});
test('password', 'Нужна цифра', () => {
enforce(/[0-9]/.test(data.password));
});
Преимущества:
Vest предоставляет механизмы управления выполнением тестов.
Ошибка — ручное управление через if.
Плохой пример:
if (shouldValidateEmail) {
test('email', 'Некорректный email', () => {
enforce(data.email).matches(emailRegex);
});
}
Правильнее:
import { skipWhen } from 'vest';
skipWhen(!shouldValidateEmail, () => {
test('email', 'Некорректный email', () => {
enforce(data.email).matches(emailRegex);
});
});
Либо:
only('email');
Использование встроенных механизмов делает поведение suite предсказуемым.
Частая ошибка — обязательная проверка необязательных полей.
Некорректно:
test('middleName', 'Минимум 2 символа', () => {
enforce(data.middleName.length >= 2);
});
Если поле пустое, тест упадёт.
Корректный вариант:
test('middleName', 'Минимум 2 символа', () => {
if (!data.middleName) {
return;
}
enforce(data.middleName.length >= 2);
});
Повторяющиеся проверки быстро усложняют кодовую базу.
Плохой пример:
test('email', 'Некорректный email', () => {
enforce(data.email).matches(emailRegex);
});
test('backupEmail', 'Некорректный email', () => {
enforce(data.backupEmail).matches(emailRegex);
});
Лучше выносить общие проверки:
function validateEmail(value) {
enforce(value).matches(emailRegex);
}
test('email', 'Некорректный email', () => {
validateEmail(data.email);
});
test('backupEmail', 'Некорректный email', () => {
validateEmail(data.backupEmail);
});
Огромные suite становятся трудно поддерживаемыми.
Признаки проблемы:
create;Плохая структура:
const suite = create((data) => {
// регистрация
// профиль
// настройки
// платежи
// уведомления
});
Лучше разделять ответственность:
export const registrationSuite = create(...);
export const profileSuite = create(...);
export const paymentSuite = create(...);
Некоторые разработчики используют warning как
полноценную ошибку.
Неверно:
warning('email', 'Email обязателен', () => {
enforce(data.email).isNotBlank();
});
Warning не блокирует форму.
Для обязательных полей нужен test:
test('email', 'Email обязателен', () => {
enforce(data.email).isNotBlank();
});
warning подходит для:
При большом количестве тестов без структуры код становится нечитаемым.
Плохо:
test(...);
test(...);
test(...);
test(...);
test(...);
Лучше логически группировать:
// username
test(...);
test(...);
// password
test(...);
test(...);
// profile
test(...);
test(...);
Частая причина runtime-ошибок:
test('tags', 'Минимум один тег', () => {
enforce(data.tags.length > 0);
});
Если tags отсутствует:
Cannot read property 'length' of undefined
Правильно:
test('tags', 'Минимум один тег', () => {
enforce(Array.isArray(data.tags));
enforce(data.tags.length > 0);
});
Некоторые разработчики пытаются выбрасывать ошибки вручную.
Плохой вариант:
test('username', 'Ошибка', () => {
if (!data.username) {
throw new Error('Username required');
}
});
Vest не предназначен для такого сценария.
Правильно:
test('username', 'Username required', () => {
enforce(data.username).isNotBlank();
});
При работе со списками часто возникают проблемы с идентификацией полей.
Некорректно:
data.users.forEach(user => {
test('email', 'Некорректный email', () => {
enforce(user.email).matches(emailRegex);
});
});
Все тесты используют одинаковое имя поля.
Корректный подход:
data.users.forEach((user, index) => {
test(`users.${index}.email`, 'Некорректный email', () => {
enforce(user.email).matches(emailRegex);
});
});
Иногда асинхронные проверки делают интерфейс медленным.
Плохой пример:
test('username', 'Имя занято', async () => {
const result = await api.checkUsername(data.username);
enforce(result.available);
});
Если запрос выполняется на каждый ввод символа, возникают:
Обычно требуется:
Иногда suite вызывается, но результат не анализируется.
Ошибка:
suite(data);
submitForm();
Правильно:
const result = suite(data);
if (result.hasErrors()) {
return;
}
submitForm();
Ошибка использования API:
if (result.getErrors()) {
return;
}
getErrors() возвращает объект, который всегда
truthy.
Правильно:
if (result.hasErrors()) {
return;
}
Получение ошибок:
const errors = result.getErrors();
Проверка наличия ошибок:
result.hasErrors();
Иногда разработчики используют внешние изменяемые переменные.
Плохой пример:
let validationCounter = 0;
const suite = create((data) => {
validationCounter++;
test('field', 'Ошибка', () => {
enforce(data.field).isNotBlank();
});
});
Такой код приводит к трудноуловимым багам, особенно в React Strict Mode.
Suite должен быть максимально чистым и детерминированным.
Слишком общие имена усложняют поддержку.
Плохо:
test('value', 'Ошибка', () => {});
test('input', 'Ошибка', () => {});
Лучше:
test('billing.address.street', 'Улица обязательна', () => {});
Хорошие имена полей:
Не каждая проверка должна быть асинхронной.
Плохой пример:
test('email', 'Некорректный email', async () => {
enforce(data.email).matches(emailRegex);
});
Async создаёт дополнительную нагрузку.
Синхронные проверки должны оставаться синхронными:
test('email', 'Некорректный email', () => {
enforce(data.email).matches(emailRegex);
});
Плохие сообщения:
test('password', 'invalid', () => {});
Либо:
test('password', 'Error 123', () => {});
Хорошие сообщения:
test('password', 'Пароль должен содержать минимум 8 символов', () => {});
Качественное сообщение: