Robkwood
Robkwood

Reputation: 365

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

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?

Upvotes: 7

Views: 17246

Answers (4)

Jota Pardo
Jota Pardo

Reputation: 878

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,
  },
});

Upvotes: 0

Ashraf Frotan
Ashraf Frotan

Reputation: 21

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: [] },
        ],
         }
      };

Upvotes: 0

Valentine Shi
Valentine Shi

Reputation: 7810

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.

Upvotes: 0

skirtle
skirtle

Reputation: 29112

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.

Upvotes: 20

Related Questions