如何测试从 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 getter currentPosition,如果它们返回一个空对象,我想显示一个额外的 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 中的计算属性上未从观察者更新数据变量

如何监视 vuex 订阅者中的方法?

如何在 Vuetify 组件中使用 Vue v-model 绑定以及计算属性和 Vuex?

如何将计算属性中的 vuex 数据传递给 data prop

vue3—reactive如何更改属性

vue中如何监听vuex中的数据变化