Как протестировать наблюдателя Vue, который наблюдает за вычисляемым свойством из VueX?

Предположим, у меня есть следующий компонент:

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


person yxu296    schedule 22.03.2018    source источник
comment
Если вы зафиксируете изменение в Vuex, вычисленное свойство обновится через Vue. Я не знаю, сработают ли часы, но установка состояния Vuex, а не изменение вычисленного напрямую, похоже, должна работать.   -  person clay    schedule 22.03.2018
comment
но это идет вразрез с духом модульного теста - он больше похож на тест e2e. Вам не нужно имитировать функциональность vueX только при тестировании компонента, подключенного к VueX.   -  person yxu296    schedule 22.03.2018
comment
Вам следует попробовать прочитать это: vue-test-utils.vuejs. org / guides / using-with-vuex.html   -  person lucas    schedule 02.08.2019
comment
Учитывая, что mapState Vuex является входом для вашего компонента, вы должны высмеивать это. Использование setComputed не позволяет правильно протестировать ваше приложение   -  person Phil    schedule 02.09.2019
comment
нет ничего плохого в импорте / имитации магазина VueX для модульных тестов. обычно вы бы издевались над ними в beforeEach(), так что ваши it() блоки скудны. по мере роста вашего компонента вы будете делать dispatch и, возможно, наблюдать за самим состоянием, поэтому будет очень неудобно изменять / устанавливать эти атрибуты для каждого теста.   -  person ierdna    schedule 26.11.2019


Ответы (4)


От того, что вы пытаетесь достичь

При тестировании я хочу убедиться, что externalDependency.doThing () вызывается с помощью bar (который исходит из состояния vuex) следующим образом:

(и это действительно чистый подход к модульному тестированию), вы можете просто принудительно изменить этот наблюдатель, который в основном является функцией. Нет необходимости отслеживать, изменяется ли наблюдатель в случае изменения вычисленного значения или значения данных - позвольте Vue обработать это. Итак, чтобы изменить наблюдателя в смонтированном экземпляре Vue, просто назовите его как

wrapper.vm.$options.watch.bar.call(wrapper.vm)

Где bar - имя вашего наблюдателя. Таким образом, вы сможете протестировать именно ту функциональность, которую хотите протестировать.

Идея взята из этого комментария https://github.com/vuejs/vue-test-utils/issues/331#issuecomment-382037200, по проблеме vue-test-utils, упомянутой вами в вопросе.

person Andrew Miroshnichenko    schedule 06.09.2020

Документация 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');
  });
})
person flup    schedule 27.11.2019
comment
У меня это не работает. Наблюдатель никогда не вызывается. - person oemera; 11.12.2020

Вам понадобится какой-то мутатор в экземпляре VueX, да, это вводит в тест еще один несвязанный модуль, но лично вашим тестом, включая использование Vuex, эта концепция уже нарушена.

Неожиданное изменение состояния более склонно к поведению, отличному от фактического использования.

person rosscooper    schedule 10.09.2019
comment
Привет, Росскупер и все остальные. Я сталкиваюсь с аналогичной проблемой, и у меня довольно сложная настройка различных компонентов, которые обмениваются данными через vuex. Один компонент наблюдает, как в заголовке указано свойство состояния (в моем случае - возвращаемое значение геттера vuex). Но я не хочу просто проверять, вызывается ли функция-наблюдатель, я хочу убедиться, что пользовательский интерфейс компонента показывает правильное состояние после того, как наблюдатель был запущен с определенным значением. Очень упрощенный пример: у меня есть геттер vuex currentPosition, и если они возвращают пустой объект, я хочу показать дополнительный элемент пользовательского интерфейса. - person Merc; 18.11.2019

Вы можете установить значение прямо в источнике, то есть 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');
});

Вы также можете вызвать метод dispatch('setBar', 'baz') в магазине, чтобы мутация происходила правильно, вместо того, чтобы напрямую устанавливать состояние.

NB Важно повторно инициализировать свое состояние для каждого монтирования (т. е. либо сделать клон, либо повторно объявить его). В противном случае один тест может изменить состояние, и следующий тест начнется с этого грязного состояния, даже если оболочка была уничтожена.

person ierdna    schedule 26.11.2019