Search code examples
vue.jsvuejs2jestjsvuexvue-test-utils

"[vuex] state field foo was overridden by a module with the same name at foobar" with deepmerge helper function in jest


I'm using a helper function to create a store inside my jests. The helper function uses deepmerge to merge the basic configuration with a customized configuration. This results in multiple console warnings

[vuex] state field "cart" was overridden by a module with the same name at "cart"
[vuex] state field "customer" was overridden by a module with the same name at "customer"
[vuex] state field "checkout" was overridden by a module with the same name at "checkout"

store.js (Reduced to a minimum for presentation purpose)

import cart from './modules/cart'
import checkout from './modules/checkout'
import customer from './modules/customer'

Vue.use(Vuex)

export const config = {
    modules: {
        cart,
        customer,
        checkout,
    },
}

export default new Vuex.Store(config)

test-utils.js

import merge from 'deepmerge'
import { config as storeConfig } from './vuex/store'

// merge basic config with custom config
export const createStore = config => {
    const combinedConfig = (config)
        ? merge(storeConfig, config)
        : storeConfig
    return new Vuex.Store(combinedConfig)
}

making use of the helper function inside

somejest.test.js

import { createStore } from 'test-utils'

const wrapper = mount(ShippingComponent, {
    store: createStore({
        modules: {
            checkout: {
                state: {
                    availableShippingMethods: {
                        flatrate: {
                            carrier_title: 'Flat Rate',
                        },
                    },
                },
            },
        },
    }),
    localVue,
})

How do I solve the console warning?


Solution

  • I believe the warning is somewhat misleading in this case. It is technically true, just not helpful.

    The following code will generate the same warning. It doesn't use deepmerge, vue-test-utils or jest but I believe the root cause is the same as in the original question:

    const config = {
      state: {},
    
      modules: {
        customer: {}
      }
    }
    
    const store1 = new Vuex.Store(config)
    const store2 = new Vuex.Store(config)
    <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/vuex.js"></script>

    There are two key parts of this example that are required to trigger the warning:

    1. Multiple stores.
    2. A root state object in the config.

    The code in the question definitely has multiple stores. One is created at the end of store.js and the other is created by createStore.

    The question doesn't show a root state object, but it does mention that the code has been reduced. I'm assuming that the full code does have this object.

    So why does this trigger that warning?

    Module state is stored within the root state object. Even though the module in my example doesn't explicitly have any state it does still exist. This state will be stored at state.customer. So when the first store gets created it adds a customer property to that root state object.

    So far there's no problem.

    However, when the second store gets created it uses the same root state object. Making a copy or merging the config at this stage won't help because the copied state will also have the customer property. The second store also tries to add customer to the root state. However, it finds that the property already exists, gets confused and logs a warning.

    There is some coverage of this in the official documentation:

    https://vuex.vuejs.org/guide/modules.html#module-reuse

    The easiest way to fix this is to use a function for the root state instead:

    state: () => ({ /* all the state you currently have */ }),
    

    Each store will call that function and get its own copy of the state. It's just the same as using a data function for a component.

    If you don't actually need root state you could also fix it by just removing it altogether. If no state is specified then Vuex will create a new root state object each time.