Let’s say you want to unit test a component which behavior depends on a Vuex getter.
Let’s use the imaginary example of a Pizza
component with a isVegan
computed property that depends on the recipe
Vuex getter: a margherita pizza is not vegan, but a zucchini pizza is.
You don’t want to use the “real” getter, because that would not be a unit test anymore, so you mock it, and all goes well at first:
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import Pizza from './Pizza.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
const recipeMock = jest.fn();
recipeMock.mockReturnValue('margherita');
const store = new Vuex.Store({
getters: {
recipe: recipeMock,
},
});
const wrapper = shallowMount(Pizza, {
localVue,
store,
});
expect(wrapper.vm.isVegan).toBeFalsy(); // pass
You move on to testing what happens when the getter updates, and with horror, you observe that the test fails:
recipeMock.mockReturnValue('zucchini');
expect(wrapper.vm.isVegan).toBeTruthy(); // fail
Trying to understand what is happening, you add logs in between your lines:
console.log(
wrapper.vm.$store.getters.recipe, // margherita
wrapper.vm.isVegan, // false
)
recipeMock.mockReturnValue('zucchini');
console.log(
wrapper.vm.$store.getters.recipe, // margherita
wrapper.vm.isVegan, // false
)
expect(wrapper.vm.isVegan).toBeTruthy(); // fail
Argh, the getter is not updating, even though we clearly ask the mock to return something else!
Why?
Because Vue is reactive: it doesn’t update anything unless it tracked an event that is explicitly changing the state of something. Sometimes, it’s hidden for convenience, like when we update data properties, but there is no such thing happening here and Vue can’t make the link between the mock updating its return value and the getter value updating.
The only way to update the getter is to commit a mutation that will impact a state property that is used in computing the getter. That would look something like this:
const localVue = createLocalVue();
localVue.use(Vuex);
const store = new Vuex.Store({
state: {
testRecipe: 'margherita',
},
mutations: {
setTestRecipe(state, value) {
state.testRecipe = value;
},
},
getters: {
recipe(state) {
return state.testRecipe;
},
},
});
Yes, it’s not perfect because we’re introducing state properties and mutations that don’t actually exist, but given the right prefix or comment to signal that, it’s an acceptable tradeoff.
And it works!
const wrapper = shallowMount(Pizza, {
localVue,
store,
});
expect(wrapper.vm.isVegan).toBeFalsy(); // pass
store.commit('setTestRecipe', 'zucchini');
expect(wrapper.vm.isVegan).toBeTruthy(); // pass
Toucan is a customer-facing analytics platform that empowers companies to drive engagement with data storytelling. Ranked the #1 easiest to use analytics solution by Gartner Peer Insights, their no-code, cloud-based platform cuts custom development costs with a quick and easy implementation, even for non-technical builders. To find out what our users say, read their reviews on Capterra.