This content originally appeared on DEV Community and was authored by Ivan Spoljaric
Cover photo by Tien Vu Ngoc on Unsplash
The problem
To find the answer to my question I went through the official Nuxt documentation and through the existing Stack Overflow and Github issue discussions.
But with no luck.
It appeared as if no one before me stumbled upon this issue (which I find somewhat hard to believe).
My AuthModule looks something like this:
@Module({
stateFactory: true,
namespaced: true,
})
export default class AuthModule extends VuexModule {
userData?: UserData | undefined = undefined;
prevRouteList: Routes[] = [];
error?: services.ICognitoError | undefined = undefined;
isLoading = false;
...
@VuexMutation
setIsLoading(isLoading: boolean) {
this.isLoading = isLoading;
}
...
@VuexAction({ rawError: true })
async register(registerData: { email: string; password: string }): Promise<any> {
this.context.commit('setIsLoading', true);
this.context.commit('setError', undefined);
this.context.commit('setInitiateRegistration', false);
this.context.dispatch('setEmail', registerData.email);
try {
const { user } = await services.register(registerData.email, registerData.password);
if (user) {
this.context.dispatch('pushPrevRoute', Routes.emailVerification);
this.context.commit('setInitiateRegistration', true);
}
} catch (error: any) {
this.context.commit('setError', error);
this.context.commit('setInitiateRegistration', false);
}
this.context.commit('setIsLoading', false);
}
...
@MutationAction
setEmail(email: string) { ... }
...
get getEmail() {
return this.email;
}
...
}
Solution
After some trial and error I finally discovered the answer to my question.
The implementation looks like this:
// auth.spec.ts
import Vuex, { Store } from 'vuex';
import { createLocalVue } from '@vue/test-utils';
import AuthModule, { IState } from './auth';
jest.mock('@/services');
const localVue = createLocalVue();
localVue.use(Vuex);
const storeOptions = {
modules: {
auth: AuthModule,
},
};
const createStore = (storeOptions: any = {}): Store<{ auth: IState }> => new Vuex.Store({ ...storeOptions });
describe('AuthModule', () => {
let store: Store<{ auth: IState }>;
beforeEach(() => {
store = createStore(storeOptions);
});
describe('mutations', () => {
// ...
it('auth/setIsLoading', () => {
expect(store.state.auth.isLoading).toBe(false);
store.commit('auth/setIsLoading', true);
expect(store.state.auth.isLoading).toBe(true);
});
// ...
});
describe('actions', () => {
// ...
it('register success', async () => {
const registerData = {
email: 'dummy@email.com',
password: 'dummy',
};
expect(store.state.auth.registrationInitiated).toBe(false);
try {
await store.dispatch('auth/register', registerData);
expect(store.state.auth.registrationInitiated).toBe(true);
} catch (error) {}
});
// ...
});
describe('mutation-actions', () => {
// ...
it('setEmail', async () => {
const dummyEmail = 'dummy@email.com';
expect(store.state.auth.email).toBe('');
await store.dispatch('auth/setEmail', dummyEmail);
expect(store.state.auth.email).toBe(dummyEmail);
});
// ...
});
describe('getters', () => {
// ...
it('auth/getError', () => {
expect(store.state.auth.error).toBe(undefined);
expect(store.getters['auth/getError']).toBe(undefined);
(store.state.auth.error as any) = 'Demmo error';
expect(store.getters['auth/getError']).toBe('Demmo error');
});
// ...
});
});
// services/auth
export async function register(email: string, password: string, attr: any = {}): Promise<any> {
try {
return await Auth.signUp({
username: email,
password,
attributes: {
...attr,
},
});
} catch (err: any) {
return Promise.reject(createError(err, 'register'));
}
}
// createError is just a util method for formatting the error message and wiring to the correct i18n label
// services/__mock__/auth
import { createError } from '../auth';
export const register = (registerData: { email: string; password: string }) => {
try {
if (!registerData) {
throw new Error('dummy error');
}
return new Promise((resolve) => resolve({ response: { user: registerData.email } }));
} catch (err) {
return Promise.reject(createError(err, 'register'));
}
};
The most important thing to realise is that the vuex-module-decorators class-based model behaves just like a vue-class-component under the hood.
All of the class-based stuff is just syntactic sugar - a wrapper around the vue-class-component API.
To quote the docs:
In your store, you use the MyModule class itself as a module...The way we use the MyModule class is different from classical object-oriented programming and similar to how vue-class-component works. We use the class itself as module, not an object constructed by the class
Another thing to keep in mind is to use createLocalVue, which enables us to use Vue classes, plugins, components etc. without polluting the global Vue class.
Adding the Vuex plugin to createLocalVue
:
localVue.use(Vuex);
The AuthModule class is declared as a Vuex (namespaced) module inside the Vuex.Store constructor (as per docs).
const storeOptions = {
modules: {
auth: AuthModule,
},
};
const createStore = (storeOptions: any = {}): Store<{ auth: IState }> => new Vuex.Store({ ...storeOptions });
In the implementation above, AuthModule (incl. store, actions, mutations, getters...) is re-created inside every test case with the help of the beforeEach
hook (to have a clean store in every new iteration).
The rest is pretty straightforward.
You can clearly see how I tested each part of the AuthModule (actions, mutations, getters..).
That's it. Happy testing :)
This content originally appeared on DEV Community and was authored by Ivan Spoljaric
Ivan Spoljaric | Sciencx (2021-09-28T14:48:34+00:00) How to unit test Vuex modules defined with the vuex-module-decorators syntax in Nuxt, using vue-test-utils and Jest?. Retrieved from https://www.scien.cx/2021/09/28/how-to-unit-test-vuex-modules-defined-with-the-vuex-module-decorators-syntax-in-nuxt-using-vue-test-utils-and-jest/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.