Библиотека Vest строит процесс валидации вокруг декларативных тестов, состояний и реактивного выполнения. При увеличении количества полей, асинхронных проверок и зависимых условий возникают ситуации, при которых часть тестов выполняется повторно, пропускается или завершается не в ожидаемом порядке. Отладка позволяет:
only, skip,
group;Основной объект в Vest — validation suite. Именно он управляет запуском тестов и хранением состояния.
Пример базового suite:
import { create, test, enforce } from 'vest';
const suite = create((data = {}) => {
test('email', 'Некорректный email', () => {
enforce(data.email).matches(/@/);
});
test('password', 'Минимум 6 символов', () => {
enforce(data.password).longerThanOrEquals(6);
});
});
Для отладки важно понимать:
Vest предоставляет API для анализа внутреннего состояния.
get()const result = suite(data);
console.log(result.get());
Пример структуры:
{
valid: false,
pending: false,
tested: ['email', 'password'],
errors: {
email: ['Некорректный email']
}
}
| Свойство | Назначение |
|---|---|
valid |
Общий статус валидности |
pending |
Есть ли незавершённые async-тесты |
tested |
Какие поля были проверены |
errors |
Ошибки по полям |
warnings |
Предупреждения |
groups |
Результаты групп |
При сложной логике часть тестов может не запускаться.
hasErrorsconst result = suite(data);
console.log(result.hasErrors('email'));
console.log(result.tested('email'));
Если вывод:
false
значит тест не выполнялся.
Частая проблема — неверные условия запуска.
if (data.role === 'admin') {
test('access', 'Нет доступа', () => {
enforce(data.access).isNotEmpty();
});
}
Если role не равен admin, тест вообще не
создаётся.
console.log(data.role);
или:
console.log(result.tested('access'));
Vest позволяет ограничивать набор выполняемых тестов.
onlyonly('email');
test('email', 'Ошибка email', () => {
enforce(data.email).matches(/@/);
});
test('password', 'Ошибка password', () => {
enforce(data.password).longerThanOrEquals(6);
});
Выполнится только email.
Разработчик забывает удалить only.
Проверка списка выполненных тестов:
console.log(result.get().tested);
skipskip('password');
test('password', 'Ошибка', () => {
enforce(data.password).isNotEmpty();
});
console.log(result.tested('password'));
Асинхронные проверки — наиболее частый источник ошибок.
test('email', 'Email уже существует', async () => {
const exists = await api.checkEmail(data.email);
enforce(exists).isFalsy();
});
Vest отслеживает незавершённые операции.
const result = suite(data);
console.log(result.pending());
Если вывод:
true
значит async-проверки ещё выполняются.
Проблема:
test('email', 'Ошибка', async () => {
await fetch('/api');
});
Ошибка возникает, если Promise никогда не завершается.
console.log(result.pending());
Если состояние постоянно true, async-тест завис.
Полезный подход — добавление логов.
test('email', 'Ошибка', async () => {
console.log('START');
const response = await fetch('/api');
console.log('RESPONSE');
const json = await response.json();
console.log('JSON');
enforce(json.valid).isTruthy();
});
При быстром вводе данных старые запросы могут завершаться позже новых.
suite({ email: 'a@mail.com' });
suite({ email: 'ab@mail.com' });
suite({ email: 'abc@mail.com' });
Первый запрос может завершиться последним.
Vest автоматически умеет отменять устаревшие проверки.
import { create, test } from 'vest';
const suite = create(async data => {
test('email', 'Ошибка', async () => {
const response = await api.validate(data.email);
return response.valid;
});
});
Проверка количества запросов:
console.count('validation request');
Vest кеширует результаты тестов.
suite({ email: 'test@mail.com' });
suite({ email: 'test@mail.com' });
Некоторые тесты могут не перезапускаться.
test('email', 'Ошибка', () => {
console.log('RUN EMAIL TEST');
enforce(data.email).matches(/@/);
});
Если лог не появляется повторно — сработал кеш.
suite.reset();
После этого:
suite(data);
все тесты будут выполнены заново.
Группы помогают организовывать валидацию.
group('auth', () => {
test('email', 'Ошибка email', () => {});
test('password', 'Ошибка password', () => {});
});
const result = suite(data);
console.log(result.get().groups);
Пример:
{
auth: {
valid: false
}
}
Vest поддерживает генерацию тестов в циклах.
data.items.forEach((item, index) => {
test(`item_${index}`, 'Ошибка', () => {
enforce(item.title).isNotEmpty();
});
});
Изменение индексов:
[
{ id: 1 },
{ id: 2 }
]
после удаления:
[
{ id: 2 }
]
Тест item_1 превращается в item_0.
Это приводит к конфликтам состояния.
Использование стабильных идентификаторов:
data.items.forEach(item => {
test(`item_${item.id}`, 'Ошибка', () => {
enforce(item.title).isNotEmpty();
});
});
Vest поддерживает предупреждения вместо ошибок.
import { warn } from 'vest';
test.memo('password_strength', () => {
warn();
});
console.log(result.get().warnings);
Сложная логика часто создаёт трудноуловимые ошибки.
if (data.enabled) {
if (data.role === 'admin') {
test('access', 'Ошибка', () => {
enforce(data.access).isNotEmpty();
});
}
}
Полезно выносить условия:
const shouldValidateAccess =
data.enabled &&
data.role === 'admin';
console.log(shouldValidateAccess);
if (shouldValidateAccess) {
test('access', 'Ошибка', () => {
enforce(data.access).isNotEmpty();
});
}
Большинство validation-ошибок возникает внутри
enforce.
enforce(data.age).greaterThan(18);
console.log(typeof data.age);
console.log(data.age);
data.age = '20';
Строка вместо числа.
enforce(Number(data.age)).greaterThan(18);
enforce(data.user.email).matches(/@/);
Ошибка:
Cannot read property 'email' of undefined
enforce(data.user?.email).matches(/@/);
или:
if (data.user) {
enforce(data.user.email).matches(/@/);
}
При сложной async-валидации полезна ручная обработка ошибок.
test('email', 'Ошибка сервера', async () => {
try {
const response = await api.validate(data.email);
enforce(response.valid).isTruthy();
} catch (e) {
console.error(e);
throw e;
}
});
Vest отлично работает с debugger.
test('email', 'Ошибка', () => {
debugger;
enforce(data.email).matches(/@/);
});
При запуске DevTools выполнение остановится внутри теста.
Иногда тесты запускаются не в ожидаемом порядке.
test('a', 'A', () => {
console.log('A');
});
test('b', 'B', () => {
console.log('B');
});
test('c', 'C', () => {
console.log('C');
});
Vest повторно валидирует только изменённые поля.
suite({ email: 'a' });
suite({ email: 'ab' });
Может выполняться только часть тестов.
test('confirm', 'Пароли не совпадают', () => {
enforce(data.confirm).equals(data.password);
});
Если изменяется password, тест confirm тоже
должен обновляться.
import { include } from 'vest';
include('confirm');
При интеграции с React состояние формы может расходиться с состоянием suite.
console.log(formState);
console.log(result.get());
Сравнение помогает обнаружить:
suite.reset();
suite.resetField('email');
console.log(result.get());
После reset ошибки должны исчезнуть.
Полезная техника — централизованный логгер.
function debugSuite(result) {
console.log({
valid: result.isValid(),
pending: result.pending(),
errors: result.getErrors(),
warnings: result.getWarnings(),
tested: result.get().tested
});
}
Использование:
const result = suite(data);
debugSuite(result);
const result = suite(formData);
выполняется на каждом render.
Это приводит к:
console.count('FORM RENDER');
const result = useMemo(() => {
return suite(formData);
}, [formData]);
При большом количестве тестов возникают задержки.
console.time('validation');
suite(data);
console.timeEnd('validation');
test('email', 'Ошибка', () => {
console.time('email test');
enforce(data.email).matches(/@/);
console.timeEnd('email test');
});
Проблемный сценарий:
useEffect(() => {
setState(suite(data));
}, [data]);
где suite вызывает изменение data.
console.count('VALIDATION');
Если счётчик растёт бесконечно — существует цикл обновлений.
Vest поддерживает композицию suite.
const authSuite = create(() => {});
const profileSuite = create(() => {});
console.log(authSuite(data).get());
console.log(profileSuite(data).get());
Раздельная проверка помогает локализовать источник ошибки.
Эффективная стратегия отладки:
Причины:
skip;if;only.Причины:
Причины:
Причины:
Причины: