Is there a way to make my vue jest test with mock vuex instead of the app store - vue.js

const localVue = createLocalVue();
localVue.use(Vuex);
describe('Dashboard component', () => {
let store;
let userDataStore;
beforeEach(() => {
userDataStore = {
namespaced: true,
state: {
sessionId: 'k5gv7lc3jvol82o91tddjtoi35kv16c3',
},
};
store = new Vuex.Store({
modules: {
userDataStore: userDataStore,
},
});
});
it('it renders the header component if there is a session id', () => {
const wrapper = shallowMount(DashboardPage, {
store,
localVue,
});
const headerComponent = wrapper.findComponent({ name: 'DashboardPage' });
expect(headerComponent.exists()).toBe(true);
});
});
but it keeps trying to access the app's main vuex and it should instead make use of the test mockup.

Related

Mocking just part of getters when importing the global store

Any idea if it is possible to mock just a getter from the global store ?
I tried this code but It does not work:
import store from '#/store';
import Vuex from "vuex";
const localVue = createLocalVue();
localVue.use(VueRouter);
localVue.use(Vuex);
const wrapper = mount(App, {
mocks: {
$store: {
getters: {
isLoggedIn: () => true // This is always returning false. I need this getter to return true value
},
}
},
store, // this is the global store for my application
localVue,
router,
});
It would be much easier just to use mocks property while mounting the component without calling localVue.use(Vuex) and without creating store instance:
const wrapper = mount(App, {
localVue,
router,
mocks: {
$store: {
getters: {
isLoggedIn: () => () => true,
}
}
}
});
I solved my problem with inspiration from the Vue Testing Handbook examples here.
import * as otherNameThanStore from '#/store'; // we need to change the name to anyone other than store
import Vuex from "vuex";
const localVue = createLocalVue();
localVue.use(VueRouter);
localVue.use(Vuex);
const store = new Vuex.Store( // attribute name must be store, otherwise typescript will throw this error: is not assignable to parameter of type 'FunctionalComponentMountOptions<Vue>'
{
state: {
...otherNameThanStore.default.state
},
getters: {
...otherNameThanStore.default.getters,
isLoggedIn: (state) => () => true,
},
}
)
const wrapper = mount(App, {
store,
localVue,
router,
});
Hope it helps other people :)

Mock of store action of vuex does not mocked

I have small vue component that on created hook dispatch some action
#Component
export default class SomeComponent extends Vue {
created() {
store.dispatch('module/myAction', { root: true });
}
}
and I wrote next test
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
const localRouter = new VueRouter();
describe('SomeComponent.vue Test', () => {
let store: any;
beforeEach(() => {
store = new Vuex.Store({
modules: {
module: {
namespaced: true,
actions: {
myAction: jest.fn()
}
}
}
});
});
it('is component created', () => {
const wrapper = shallowMount(SomeComponent, {
localVue,
store,
propsData: {}
});
expect(wrapper.isVueInstance()).toBeTruthy();
});
});
but for some reason the "real" code are executed and I got a warning
isVueInstance() is deprecated. In your test you should mock $store object and it's dispatch function. I fixed typo in created(), here's my version of SomeComponent and working test, hope that would help.
#Component
export default class SomeComponent extends Vue {
created () {
this.$store.dispatch('module/myAction', { root: true })
}
}
import { shallowMount, Wrapper } from '#vue/test-utils'
import SomeComponent from '#/components/SomeComponent/SomeComponent.vue'
let wrapper: Wrapper<SomeComponent & { [key: string]: any }>
describe('SomeComponent.vue Test', () => {
beforeEach(() => {
wrapper = shallowMount(SomeComponent, {
mocks: {
$store: {
dispatch: jest.fn()
}
}
})
})
it('is component created', () => {
expect(wrapper.vm.$store.dispatch).toBeCalled()
expect(wrapper.vm.$store.dispatch).toBeCalledWith('module/myAction', { root: true })
})
})
Also keep in mind that when you test SomeComponent or any other component you should not test store functionality, you should just test, that certain actions/mutations were called with certain arguments. The store should be tested separately. Therefore you don't need to create real Vuex Store when you test components, you just need to mock $store object.

$router.push not triggering in unit testing

I have the following problem while trying to unit test my Vue application.
Even spying and mocking $router.push, I still can't make it to be called while inside unit testing:
This is my unit testing (Home.spec.js)
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
describe('Home.vue', () => {
let actions;
let getters;
let store;
let router;
beforeEach(() => {
actions = {
[FETCH_USER_REPOSITORIES]: jest.fn()
};
getters = {
getTopThreeRepositories: jest.fn(repositoryMock.getRepositories)
};
store = new Vuex.Store({ getters, actions });
router = new VueRouter();
});
it('should redirect when 404 status code received', async () => {
jest.spyOn(store, 'dispatch').mockRejectedValue({ statusCode: 404 });
jest.spyOn(router, 'push').mockResolvedValue({});
const wrapper = await shallowMount(Home, {
store,
localVue,
router
});
expect(router.push).toHaveBeenCalledWith('/not-found');
});
});
Now, this is my Home.vue:
import { mapGetters } from 'vuex';
import { FETCH_USER_REPOSITORIES } from "../store/actions";
import RepositoryList from "#/components/RepositoryList";
import Card from "#/components/Card";
export default {
name: 'view-home',
components: {
Card,
RepositoryList
},
async beforeMount() {
try {
await this.$store.dispatch(FETCH_USER_REPOSITORIES, 'some-repo');
} catch(err) {
console.log(this.$router);
await this.$router.push('/not-found');
}
},
computed: {
...mapGetters(["getTopThreeRepositories"])
}
}
The console log shows the $router correctly, with the spy.
If I force the calling inside the unit testing, it works, but the expects always fails giving me back that $router.push hasn't been called.
Can anyone help me, please?
Thanks!
You should specify $store and $route as mocks in your mounting options, as shown below. Also there's no need to await shallowMount because shallowMount does not return a Promise, so the await would just return immediately.
describe('Home.vue', () => {
it('should redirect when 404 status code received', () => {
const $store = {
dispatch: jest.fn()
}
const $router = {
push: jest.fn()
}
const wrapper = shallowMount(Home, {
localVue,
mocks: {
$store,
$router,
}
});
expect($router.push).toHaveBeenCalledWith('/not-found');
})
})

Vuex mock store undefined in Jest unit test

I'm new to jest and am trying to write a unit test that relies on the value of a store property.
I tried to create a new mock store in my test (I already have a mock store for a different test), but for some reason, I keep getting the error
TypeError: Cannot read property 'state' of undefined
My tests are:
import { shallowMount, createLocalVue,} from '#vue/test-utils';
import userButtons from '#components/user-profile/UserButtons.vue';
import Vuex from 'vuex';
import { defaultValues, } from '#store/api/userButtons.js';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('UserButtons', () => {
let actions;
let store;
let storeIsAdded;
beforeEach(() => {
actions = {
getUserInfo: jest.fn(),
};
store = new Vuex.Store({
modules: {
userButtons: {
namespaced: true,
actions,
state: {
userButton: defaultValues,
},
mutations: {
setUserButton: jest.fn(),
},
},
},
});
storeIsAdded = new Vuex.Store({
state: {
isItemAdded: true,
},
});
});
test('vuex called on create', () => {
shallowMount(UserButtons, { store, localVue, propsData: { id: 3406304, }, });
expect(actions.getUserInfo).toHaveBeenCalled();
});
test('renders correctly', () => {
const wrapper = shallowMount(UserButtons, { store, localVue, propsData: { id: 3406304, }, });
expect(wrapper.element).toMatchSnapshot();
});
test('indicates remove if isItemAdded is true', () => {
const wrapper = shallowMount(UserButtons, { storeIsAdded, localVue, propsData: { id: 3406304, }, });
expect(wrapper.find('.button-action.my-items').text()).toBe('- Remove from My Items');
});
});
The first two tests, which just use defaultValues for my store, pass.
The last test, 'indicates remove if isItemAddedis true, is the one that fails, and is using the mock store,storeIsAdded`.
If anyone has any insight, it would be much appreciated!!!
EDIT:
Even if I modify my mock store to be more similar to the store that seems to be working, like this:
storeIsInList = new Vuex.Store({
modules: {
userButton: {
namespaced: true,
actions,
state: {
userButton: {
isItemAdded: true,
},
},
},
},
});
I get the same error.
It seems the error is when accessing the store state in the component. So, my guess is that the store maybe need to be module/namespace, i.e.
storeIsInList = new Vuex.Store({
modules: {
userButtons: {
namespaced: true,
actions,
state: {
userButton: {
isItemAdded: true,
},
},
},
},
});
And as #il0v3d0g stated, maybe the namespace name is wrong.

Vue Test Mock Promise-Based Action within Module

I unfortunately can't attach all code or create a gist because the project I'm working on is related to work but I can give enough detail that I think it will work.
I'm trying to mock a call to an action that is stored in a different module but for the life of me I can't figure out how to. I'm able to create a Jest spy on the store.dispatch method but I want to be able to resolve the promise and make sure that the subsequent steps are taken.
The method in the SFC is
doSomething(data) {
this.$store.dispatch('moduleA/moduleDoSomething',{data: data})
.then(() => {
this.$router.push({name: 'RouteName'})
})
.catch(err => {
console.log(err)
alert('There was an error. Please try again.')
})
},
This is what my test looks like:
import { mount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import Vuetify from 'vuetify'
import Component from '#/components/Component'
import moduleA from '#/store/modules/moduleA'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Vuetify)
describe('Component.vue', () => {
let actions
let store
const $router = []
beforeEach(() => {
actions = {
moduleDoSomething: jest.fn((payload) => {
return Promise.resolve({
status: 200,
})
})
}
store = new Vuex.Store({
state: {},
modules: {
moduleA: {
actions
}
},
})
})
it('does something', () => {
const wrapper = mount(Component, {
store,
localVue,
mocks: {
$router,
},
})
let button = wrapper.find('button that calls doSomething')
button.trigger('click')
expect(actions.moduleDoSomething).toHaveBeenCalled()
expect(wrapper.vm.$router[0].name).toBe('RouteName')
})
})
The following test passes, but I don't want to just test that the action was dispatched; I also want to test things in the "then" block.
it('does something', () => {
const dispatchSpy = jest.spyOn(store, 'dispatch')
const wrapper = mount(Component, {
store,
localVue,
mocks: {
$router,
},
})
let button = wrapper.find('button that calls doSomething')
button.trigger('click')
expect(dispatchSpy).toHaveBeenCalledWith('moduleA/moduleDoSomething',{data: data})
})
})
I managed to solve this problem by simply making the module namespaced in the mocked store.
store = new Vuex.Store({
state: {},
modules: {
moduleA: {
actions,
namespaced: true
}
},
})
I'll delete the question in a little bit