Unit-тестирование компонентов Vue, взаимодействующих с маршрутизатором, требует способности изолировать логику компонента от настоящей навигации. В таких случаях используются моки Vue Router, которые имитируют поведение маршрутизатора, позволяя проверять реакции компонентов на изменение маршрутов без необходимости поднимать полноценное приложение.
Для начала необходимо импортировать сам Vue Router и вспомогательные функции тестирования:
import { createRouter, createWebHistory } from 'vue-router';
import { mount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';
Создание мок-роутера подразумевает настройку маршрутов в минимальном виде:
const routes = [
{ path: '/', name: 'Home' },
{ path: '/about', name: 'About' }
];
const router = createRouter({
history: createWebHistory(),
routes
});
В unit-тестах часто не требуется настоящая навигация, поэтому
маршрутизатор можно замокать полностью, используя
только ключевые методы: push, replace,
currentRoute.
push и replace — методы, используемые для перехода по маршрутам. В тестах их удобно заменять на jest-функции:
const mockRouter = {
push: jest.fn(),
replace: jest.fn(),
currentRoute: { value: { path: '/' } }
};
Такой мок позволяет проверять, какие маршруты пытается вызвать компонент:
const wrapper = mount(MyComponent, {
global: {
mocks: {
$router: mockRouter
}
}
});
wrapper.find('button.navigate').trigger('click');
expect(mockRouter.push).toHaveBeenCalledWith({ name: 'About' });
Здесь проверяется, что клик по кнопке вызывает push с
правильным именем маршрута.
$route содержит информацию о текущем маршруте. Для
unit-тестов удобно создавать объект с необходимыми параметрами:
const mockRoute = {
path: '/about',
name: 'About',
params: { id: '123' },
query: { ref: 'test' }
};
const wrapper = mount(MyComponent, {
global: {
mocks: {
$route: mockRoute
}
}
});
Теперь внутри компонента $route.path вернёт
/about, $route.params.id — '123',
что позволяет тестировать условную логику на основе текущего
маршрута.
В Vue 3 с Composition API вместо $router и
$route часто используют useRouter и
useRoute. Для их мокирования создаются заглушки:
import * as vueRouter from 'vue-router';
jest.spyOn(vueRouter, 'useRouter').mockReturnValue(mockRouter);
jest.spyOn(vueRouter, 'useRoute').mockReturnValue(mockRoute);
Это позволяет полностью заменить реактивные объекты маршрутизатора, не изменяя исходный компонент.
Чтобы тестировать реакцию компонента на изменения маршрута,
$route можно сделать реактивным:
import { ref } from 'vue';
const routeRef = ref({
path: '/',
params: {},
query: {}
});
const mockRouteReactive = {
value: routeRef.value
};
const wrapper = mount(MyComponent, {
global: {
mocks: {
$route: mockRouteReactive
}
}
});
// Изменение маршрута в тесте
routeRef.value = { path: '/new', params: {}, query: {} };
expect(wrapper.text()).toContain('/new');
Такой подход позволяет проверять watch и
computed свойства, зависящие от текущего маршрута.
Для интеграционных unit-тестов, когда нужен реальный роутер, но без
полной истории браузера, можно использовать createRouter с
memory history:
import { createMemoryHistory } from 'vue-router';
const router = createRouter({
history: createMemoryHistory(),
routes
});
const wrapper = mount(MyComponent, {
global: {
plugins: [router]
}
});
router.push('/about');
await router.isReady();
expect(wrapper.html()).toContain('About Page');
createMemoryHistory не взаимодействует с браузером, но
позволяет проверять полную работу маршрутов внутри тестового
окружения.
push и replace должны возвращать промис,
если компонент использует await $router.push().$route с полной структурой params и
query.router-link, можно заменить
его на простой stub или мок:global: {
stubs: {
RouterLink: { template: '<a><slot /></a>' }
}
}
$route особенно важна при тестировании
watch на route или computed с
route.params.test('navigate to About on button click', async () => {
const mockRouter = { push: jest.fn() };
const wrapper = mount(MyComponent, {
global: { mocks: { $router: mockRouter } }
});
await wrapper.find('button.navigate').trigger('click');
expect(mockRouter.push).toHaveBeenCalledWith({ name: 'About' });
});
test('renders data based on route param', () => {
const mockRoute = { params: { id: '42' } };
const wrapper = mount(MyComponent, {
global: { mocks: { $route: mockRoute } }
});
expect(wrapper.text()).toContain('42');
});
Моки роутера позволяют создавать полностью изолированные unit-тесты, где проверяется логика компонентов, не затрагивая настоящую навигацию и не завися от внешних факторов браузера. Такой подход делает тесты стабильными, быстрыми и детализированными.