Vue Testing: Testing Router Hooks
Intro
It isn’t always obvious how to go about testing certain aspects of your front end code. This is especially true when it comes to the Vue router. This is even more true when it comes to testing the execution of your various router hooks.
Background
beforeEach
, afterEach
, and beforeResolve
each behave similarly in its underlying code.
If you take a look at the Vue router object you will see that for each of the respective router hooks it is making a call to the method registerHook
and passing a hook and then the registered function to be appended/pushed onto the hook array.
If you clicked on the link above and investigated the file you would have found the following:
beforeEach (fn: Function): Function {
return registerHook(this.beforeHooks, fn)
}beforeResolve (fn: Function): Function {
return registerHook(this.resolveHooks, fn)
}afterEach (fn: Function): Function {
return registerHook(this.afterHooks, fn)
}
This gives us some insight into how we can easily unit test these hooks. The vue-router
package comes with types that have been defined and on these types they do not expose the variables beforeHooks
, resolveHooks
, or afterHook
. Thankfully for our purposes they left each of these as public variables on the object.
Let’s Unit Test
Take the following router.js file at the root of a Vue application folder:
// router.jsimport Vue from 'vue';import VueRouter from 'vue-router';import Home from '@/Home.vue';import someService from '@/services/some-service';Vue.use(VueRouter);export const routes = {
mode: 'history',
routes: [{
path: '/',
name: 'home',
component: Home,
redirect: { name: 'controls-dashboard' }
}]
};const router = new VueRouter(routes);router.beforeEach((to, from, next) => {
someService(to, from, next);
});export default router;
The above router has some code that we want to test inside of the beforeEach
that will be called when invoked: someService
.
To be able to unit test whether someService
gets called properly, we have to access the beforeHooks
variable:
import router, { routes } from '@/router';import someService from '@/services/some-service';//using jest to mock
jest.mock('@/services/some-service');describe('when router.beforeEach is called', () => {
it('then someService is called', () => {
const to = jest.fn();
const from = jest.fn();
const next = jest.fn(); router.beforeHooks.forEach((hook) => {
hook(to, from, next);
});
expect(someService).toHaveBeenCalledWith(to, from, next); });
});
The reason we do forEach instead of just referencing the zeroth index, is because you can have multiple beforeEach hooks.
Conclusion
You can do the same for beforeResolve
and afterEach
as well by iterating over resolveHooks
and afterHooks
, respectively.