如何测试从 VueX 监视计算属性的 Vue watcher?
Posted
技术标签:
【中文标题】如何测试从 VueX 监视计算属性的 Vue watcher?【英文标题】:How to test Vue watcher that watches a computed property from VueX? 【发布时间】:2018-09-01 01:44:16 【问题描述】:假设我有以下组件:
import mapState from 'vuex';
import externalDependency from '...';
export default
name: 'Foo',
computed:
...mapState(['bar'])
,
watch:
bar ()
externalDependency.doThing(this.bar);
在测试时,我想确保externalDependency.doThing()
被bar
(来自vuex 状态)调用,如下所示:
it('should call externalDependency.doThing with bar', () =>
const wrapper = mount(Foo);
const spy = jest.spyOn(externalDependency, 'doThing');
wrapper.setComputed(bar: 'baz');
expect(spy).toHaveBeenCalledWith('baz');
);
Vue test-utils 有一个 setComputed 方法,可以让我当前测试它,但我不断收到警告说 setComputed 将很快被删除,我不知道还有什么方法可以测试它:
https://github.com/vuejs/vue-test-utils/issues/331
【问题讨论】:
如果你提交了对 Vuex 的更改,计算的属性将通过 Vue 更新。我不知道手表是否会触发,但是设置 Vuex 状态而不是直接更改计算似乎应该可以工作。 但这与单元测试的精神背道而驰——它更像是一个 e2e 测试。您不必在测试 VueX 连接组件时模拟 vueX 功能 您应该尝试阅读以下内容:vue-test-utils.vuejs.org/guides/using-with-vuex.html 鉴于 Vuex 的mapState
是您组件的输入,这就是您应该模拟的内容。使用 setComputed
无法正确测试您的应用
导入/模拟 VueX 商店进行单元测试没有任何问题。通常你会在beforeEach()
中模拟它们,所以你的it()
块很精简。随着组件的增长,您可能会执行dispatch
并可能观察状态本身,因此为每个测试更改/设置这些属性会变得非常尴尬。
【参考方案1】:
从你想要达到的目标
在测试时,我想确保使用 bar(来自 vuex 状态)调用 externalDependency.doThing(),如下所示:
(这确实是纯粹的单元测试方法),你可以强制改变这个观察者,这基本上是一个函数。 如果计算值或数据值发生更改,则无需跟踪观察者是否正在更改 - 让 Vue 处理它。 因此,要更改已挂载的 Vue 实例中的观察者,只需将其称为
wrapper.vm.$options.watch.bar.call(wrapper.vm)
bar
是你的观察者的名字。通过这种方式,您将能够测试您要测试的确切功能。
您在问题中提到的关于 vue-test-utils 问题的这条评论 https://github.com/vuejs/vue-test-utils/issues/331#issuecomment-382037200 的想法。
【讨论】:
【参考方案2】:Vue Test Utils 文档指出了一种使用非常简单的 Vuex 存储的不同方法:
import shallowMount, createLocalVue from '@vue/test-utils'
import Vuex from 'vuex'
// use a localVue to prevent vuex state from polluting the global Vue instance
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Foo.vue', () =>
let state;
let store;
beforeEach(() =>
// create a new store for each test to prevent pollution
state = bar: 'bar' ;
store = new Vuex.Store( state );
)
it('should call externalDependency.doThing with bar', () =>
shallowMount(MyComponent, store, localVue );
const spy = jest.spyOn(externalDependency, 'doThing');
// trigger the watch
state.bar = 'baz';
expect(spy).toHaveBeenCalledWith('baz');
);
)
【讨论】:
这对我不起作用。观察者永远不会被调用。【参考方案3】:您将需要在 VueX 实例上使用某种 mutator,是的,这确实会在测试中引入另一个不相关的单元,但是根据您的测试,包括使用 Vuex,这个概念已经被打破了。
以意想不到的方式修改状态更容易导致与实际使用不同的行为。
【讨论】:
你好 rosscooper 和其他所有人。我面临一个类似的问题,我有一个相当复杂的不同组件设置,它们通过 vuex 进行通信。一个组件监视,就像标题所说的状态属性(在我的例子中是 vuex getter 的返回值)。但我不想只测试是否调用了 watcher 函数,我想确保在使用特定值触发 watcher 后组件的 UI 显示正确的状态。非常简化的示例:我有一个 vuex gettercurrentPosition
,如果它们返回一个空对象,我想显示一个额外的 UI 元素。【参考方案4】:
您可以直接在源设置值,即 VueX。所以你的 store.js 中会有这样的东西:
const state =
bar: 'foo',
;
const mutations =
SET_BAR: (currentState, payload) =>
currentState.bar = payload;
,
;
const actions =
setBar: ( commit , payload) =>
commit('SET_BAR', payload);
,
;
export const mainStore =
state,
mutations,
actions,
;
export default new Vuex.Store(mainStore);
然后在你的 component.spec.js 中你会这样做:
import mainStore from '../store';
import Vuex from 'vuex';
//... describe, and other setup functions
it('should call externalDependency.doThing with bar', async () =>
const localState =
bar: 'foo',
;
const localStore = new Vuex.Store(
...mainStore,
state: localState,
);
const wrapper = mount(Foo,
store: localStore,
);
const spy = jest.spyOn(externalDependency, 'doThing');
localStore.state.bar = 'baz';
await wrapper.vm.$nextTick();
expect(spy).toHaveBeenCalledWith('baz');
);
您也可以在 store 上调用 dispatch('setBar', 'baz')
方法并让突变正确发生,而不是直接设置状态。
NB 为每次挂载重新初始化您的状态很重要(即进行克隆或重新声明)。否则,一个测试可以更改状态,下一个测试将从该脏状态开始,即使包装器已被破坏。
【讨论】:
以上是关于如何测试从 VueX 监视计算属性的 Vue watcher?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Vuex 在 Vue.js 中的计算属性上未从观察者更新数据变量
如何在 Vuetify 组件中使用 Vue v-model 绑定以及计算属性和 Vuex?