Vuex with Jest - this.$store.getters.<getterName> is not a function
Asked Answered
N

4

7

I'm working on a survey builder in Vue, and survey questions which the user creates are committed to Vuex so they can be retrieved later like so:

computed: {
  inputs() {
    return this.$store.getters.questions(this.pageNumber);
  },
},

pageNumber is a prop the component receives and inputs() returns an array of questions. This all seems to work in terms of rendering the correct questions on screen but I'm having trouble with Jest tests.

In order to test I was hoping I could mock the store with getters like my attempt below (omitting some parts):

const localVue = createLocalVue();
localVue.use(Vuex);

beforeEach(() => {
  state = {
    survey: {
      pages: [
        // pages objects
      ],
    },
  };

  getters = {
    questions: () => [
      { type: 'Radio', config: { label: 'Test label', options: [{ label: 'Test option label' }] }, validation: [] },
    ],
  };

  store = new Vuex.Store({
    state,
    getters,
  });
});

But this results in the error:

TypeError: this.$store.getters.questions is not a function

However, removing that arrow function from getters.questions gives me:

[vuex] getters should be function but "getters.questions" is [{"type":"Radio","config":{"label":"Test label","options":[{"label":"Test option label"}]},"validation":[]}].

So I think I could be completely misunderstanding. Could someone point me in the right direction?

Neoimpressionism answered 28/8, 2018 at 12:35 Comment(0)
F
20

The getters of a store are just like computed properties on components, they are defined using functions but accessed as properties, without the parentheses.

Given this line:

return this.$store.getters.questions(this.pageNumber);

it would appear that your questions getter is returning a function that accepts a pageNumber. That isn't what you're currently defining in your test getter, you're just returning an array.

So either the invocation needs to change to use square brackets:

return this.$store.getters.questions[this.pageNumber];

or the getter needs to return a function:

getters = {
    questions: () => () => [
        { type: 'Radio', config: { label: 'Test label', options: [{ label: 'Test option label' }] }, validation: [] }
    ]
};

If it helps to clarify, this is equivalent to:

getters = {
    questions: function () {
        return function () {
            const questions = [
                { type: 'Radio', config: { label: 'Test label', options: [{ label: 'Test option label' }] }, validation: [] }
            ];

            return questions;
        };
    }
};

Note that I'm completely ignoring the passed pageNumber as I assume your test getter is hard-coded to return the correct array of questions.

You may wish to consult with the non-test version of this getter as I expect you'll see it returns an extra level of function.

Flagging answered 28/8, 2018 at 14:29 Comment(2)
Thanks very much for this, that makes sense and you're right that my getter returns a function. Modifying the invocation would break a few things so I've decided to modify the test getter to return a function as you suggested, and am now getting "TypeError: Cannot read property 'questions' of undefined". I presume this is to do with the way I've structured my store/getters? However it seems to be working in the actual UI of the application. Apologies, I'm probably missing something obvious!Neoimpressionism
"the getter needs to return a function": helped me in 2022.Editheditha
P
0

Under vue-test-utils and Jest you could easily mock a getter (especially deep nested module getter) if you replace your mocked store with the following:

Approach #1
store: new Vuex.Store({
    getters: {
        'top-level-module/nested-module-level-1/more-nested-module/dataItem': function () {
            return 'any fake data you want'
        }
    }
}),

This soulution meakes you free from writing deep nested store objects mocks if you just want to test a component on a mocked getter (say you obtain value from the nested store module in your component's computed). Just return the fake (!) object of your preference from the mock getter.

Note you do not need to mock store state if you do not test it.

As well do not test the getter's returned object in this test. Extract it to the separate dedicated test.

Approach #2

You would not even need Vuex. Just use vue-test-utils mocks as follows:

mocks: {
    $store: {
        getters: {
            'top-level-module/nested-module-level-1/more-nested-module/dataItem': (function () {
                return 'any fake data you want'
            })()
        }
    }
},

Note that vs Approach #1 you have to make your mock getter to be an IIFE to get the value from mock as the mock does not invoke getter as Vuex does.

Pharmaceutical answered 15/9, 2021 at 9:3 Comment(0)
F
0

Write the questions method in getters like this:

    getters = {
        questions: (state) => (page_number)=>{ [
          { type: 'Radio', config: { label: 'Test 
               label', 
           options: [{ label: 'Test option label' }] 
           }, 
           validation: [] },
        ],
         }
      };
Fecal answered 25/2, 2022 at 18:58 Comment(1)
While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.Arak
A
0

In my case, I created a new module but I forgot to register it in the index.js file.

New file in src/store/modules: entities.js

Then in the index.js file:

import entities from "./modules/entities";

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    ...
    entities,
  },
});
Amersfoort answered 10/11, 2022 at 16:37 Comment(1)
In other case, I was adding a mapField from an inexisting module because I wrote in the bad way the name of it... The correct name was "forms" and I put in a bad way "froms"Amersfoort

© 2022 - 2024 — McMap. All rights reserved.