Навигационные хуки Vue Router — это специальные функции, которые выполняются в процессе перехода между маршрутами. Они позволяют управлять доступом к страницам, выполнять асинхронные операции перед рендерингом компонентов, а также контролировать поведение маршрутизатора в целом. Тестирование этих хуков является ключевым для обеспечения корректной работы маршрутизации в приложении.
Vue Router предоставляет несколько видов хуков:
Global Before Hooks
(router.beforeEach) Выполняются перед каждым
переходом маршрута. Позволяют проверять условия доступа, загружать
данные или перенаправлять пользователя.
Global Resolve Hooks
(router.beforeResolve) Срабатывают после всех
beforeEach и перед финальной загрузкой компонента. Часто
используются для окончательной подготовки данных перед
отображением.
Global After Hooks
(router.afterEach) Выполняются после завершения
перехода маршрута. Не могут отменять навигацию, но позволяют, например,
обновлять метаданные страницы.
Per-Route Guards (beforeEnter)
Определяются на уровне конкретного маршрута. Предназначены для проверки
условий доступа или подготовки данных для конкретного пути.
In-Component Guards (beforeRouteEnter,
beforeRouteUpdate, beforeRouteLeave)
Определяются внутри компонентов и дают возможность контролировать
поведение при входе, обновлении или уходе с маршрута. Отличие в том, что
beforeRouteEnter не имеет доступа к экземпляру компонента,
пока он не будет создан.
Для тестирования навигационных хуков важно имитировать поведение Vue
Router без необходимости полноценного запуска всего приложения. Для
этого создается мок объекта router, с
которым можно:
to и
from объекты маршрутов.push, replace)
для проверки перенаправлений.Пример структуры мока:
const mockRouter = {
push: jest.fn(),
replace: jest.fn(),
beforeEachHooks: [],
beforeEach(fn) {
this.beforeEachHooks.push(fn);
}
};
Глобальные хуки требуют проверки логики в различных сценариях переходов:
to и from:const to = { path: '/dashboard', meta: { requiresAuth: true } };
const from = { path: '/login' };
let nextCalled = false;
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
nextCalled = true;
}
});
router.beforeEachHooks.forEach(hook => hook(to, from, () => {}));
expect(nextCalled).toBe(true);
router.beforeEach((to, from, next) => {
if (!isAuthenticated()) next('/login');
else next();
});
const nextMock = jest.fn();
hook(to, from, nextMock);
expect(nextMock).toHaveBeenCalledWith('/login');
Для per-route guards достаточно вызвать функцию
beforeEnter напрямую, передав моки маршрутов и
next. Ин-компонентные хуки тестируются чаще всего через
монтирование компонента с использованием Vue Test
Utils, где можно:
next и изменение состояния
компонента.Пример для beforeRouteLeave:
const wrapper = mount(MyComponent, { global: { mocks: { $router: mockRouter } } });
const leaveHook = wrapper.vm.$options.beforeRouteLeave[0];
const nextMock = jest.fn();
leaveHook.call(wrapper.vm, { path: '/home' }, { path: '/dashboard' }, nextMock);
expect(nextMock).toHaveBeenCalled();
Навигационные хуки часто используют асинхронные операции, например, проверку авторизации через API. Для тестов это важно:
async/await или возвращать
Promise в хуках.next после завершения асинхронной
операции.router.beforeEach(async (to, from, next) => {
const user = await fetchUser();
if (!user) next('/login');
else next();
});
В тесте можно сделать:
jest.mock('./api', () => ({ fetchUser: jest.fn(() => Promise.resolve(null)) }));
await hook(to, from, nextMock);
expect(nextMock).toHaveBeenCalledWith('/login');
to и from объектов для разных
сценариев.next вызывается
корректно.beforeRouteEnter нельзя обращаться к
this, поэтому проверку состояния компонента нужно делать
через колбек next(vm => { ... }).beforeRouteUpdate важно тестировать как
обновление параметров, так и смену query-параметров.beforeRouteLeave можно тестировать предотвращение
ухода, передавая false в next.Тщательное тестирование навигационных хуков позволяет гарантировать, что переходы между страницами работают корректно, а бизнес-логика, связанная с авторизацией, загрузкой данных и перенаправлениями, всегда выполняется предсказуемо.