Как протестировать глобальную шину событий в VueJS - PullRequest
5 голосов
/ 21 февраля 2020

В этой статье объясняется, как использовать глобальную шину событий в VueJS. Он описывает альтернативный метод обычному подходу, использующему шину событий, определенную в отдельном файле:

import Vue from 'vue';

const EventBus = new Vue();
export default EventBus;

. Он должен быть импортирован в каждый SF C, где это необходимо. Альтернативный подход присоединяет шину глобальных событий к основному экземпляру Vue:

// main.js
import Vue from 'vue';

Vue.prototype.$eventBus = new Vue(); // I call it here $eventBus instead of $eventHub

new Vue({
  el: '#app',
  template: '<App/>',
});

// or alternatively
import Vue from 'vue';
import App from './App.vue';

Vue.prototype.$eventBus = new Vue();

new Vue({
  render: (h): h(App),
}).$mount('#app');

Теперь у меня проблема в том, что я не знаю, как использовать шину глобальных событий, созданную таким образом в модуле tests.

Уже существует вопрос о тестировании шины глобальных событий с использованием первого упомянутого подхода, но ответ не принимается.

Я попытался, как предложено в одном из отвечает на использование createLocalVue, но это не помогло:

it('should listen to the emitted event', () => {
  const wrapper = shallowMount(TestingComponent, { localVue });
  sinon.spy(wrapper.vm, 'handleEvent');
  wrapper.vm.$eventBus.$emit('emit-event');
  expect(wrapper.vm.handleEvent.callCount).to.equal(1);
});

Это говорит о ожидаемом 0, фактическом 1. Я пробовал с async функцией и $nextTick(), но без успеха.

Для предыдущего примера я использую mocha, chai и sinon. Это только для иллюстрации. Ответы с использованием jest или любой другой инфраструктуры тестирования / библиотеки утверждений приветствуются.

РЕДАКТИРОВАТЬ 25 февраля 2020 года

Чтение книги "Тестирование Vue. js Приложения " от Эдда Йербурга, автора @vue/test-utils, я выдвинул некоторые идеи, но я все еще пытаюсь понять, как выполнить sh тестирование шины глобального события, добавленной как свойство экземпляра. В книге свойства экземпляра проверяются в модульных тестах.

Я создал git хранилище с примером кода, следующим за статьей от medium.com . Для этого примера я использовал jest для модульного тестирования.

Это код:

src/main.js

import Vue from 'vue';
import App from './App.vue';

<strong>// create global event bus as instance property
Vue.prototype.$eventBus = new Vue();</strong>

Vue.config.productionTip = false;

new Vue({
  render: (h) => h(App),
}).$mount('#app');

src/App.vue

<template>
  <div id="app">
    <hello-world></hello-world>
    <change-name></change-name>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';
import ChangeName from './components/ChangeName.vue';

export default {
  name: 'App',
  components: {
    HelloWorld,
    ChangeName,
  },
};
</script>

src/components/HelloWorld.vue

<template>
  <div>
    <h1>Hello World, I'm {{ name }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      name: 'Foo',
    };
  },
  created() {
    this.$eventBus.$on('change-name', this.changeName);
  },
  beforeDestroy() {
    this.$eventBus.$off('change-name');
  },
  methods: {
    changeName(name) {
      this.name = name;
    },
  },
};
</script>

src/components/ChangeName.vue Изменить имя

<script>
export default {
  name: 'ChangeName',
  data() {
    return {
      newName: '',
    };
  },
  methods: {
    changeName() {
      this.$eventBus.$emit('change-name', this.newName);
    },
  },
};
</script>

Это очень простое приложение с двумя компонентами. Компонент ChangeName.vue имеет элемент ввода, и пользователь может вызвать метод, нажав кнопку. Метод генерирует событие change-name с использованием глобальной шины событий. Компонент HelloWorld.vue прослушивает событие change-name и обновляет свойство модели name.

. Вот как я пытался его протестировать:

tests\unit\HelloWorld.spec.js

import { shallowMount } from '@vue/test-utils';
import HelloWorld from '@/components/HelloWorld.vue';

describe('HelloWorld.vue', () => {
  const mocks = {
    $eventBus: {
      $on: jest.fn(),
      $off: jest.fn(),
      $emit: jest.fn(),
    },
  };

  it('listens to event change-name', () => {
    <strong>// this test passes</strong>
    const wrapper = shallowMount(HelloWorld, {
      mocks,
    });
    expect(wrapper.vm.$eventBus.$on).toHaveBeenCalledTimes(1);
    expect(wrapper.vm.$eventBus.$on).toHaveBeenCalledWith('change-name', wrapper.vm.changeName);
  });

  it('removes event listener for change-name', () => {
    <strong>// this test does not pass</strong>
    const wrapper = shallowMount(HelloWorld, {
      mocks,
    });
    expect(wrapper.vm.$eventBus.$off).toHaveBeenCalledTimes(1);
    expect(wrapper.vm.$eventBus.$off).toHaveBeenCalledWith('change-name');
  });

  it('calls method changeName on event change-name', () => {
    <strong>// this test does not pass</strong>
    const wrapper = shallowMount(HelloWorld, {
      mocks,
    });
    jest.spyOn(wrapper.vm, 'changeName');
    wrapper.vm.$eventBus.$emit('change-name', 'name');
    expect(wrapper.vm.changeName).toHaveBeenCalled();
    expect(wrapper.vm.changeName).toHaveBeenCalledWith('name');
  });
});

tests\unit\ChangeName.spec.js

import { shallowMount } from '@vue/test-utils';
import ChangeName from '@/components/ChangeName.vue';

describe('ChangeName.vue', () => {
  const mocks = {
    $eventBus: {
      $on: jest.fn(),
      $off: jest.fn(),
      $emit: jest.fn(),
    },
  };

  it('emits an event change-name', () => {
    <strong>// this test passes</strong>
    const wrapper = shallowMount(ChangeName, {
      mocks,
    });
    const input = wrapper.find('input');
    input.setValue('name');
    const button = wrapper.find('button');
    button.trigger('click');
    expect(wrapper.vm.$eventBus.$emit).toHaveBeenCalledTimes(1);
    expect(wrapper.vm.$eventBus.$emit).toHaveBeenCalledWith('change-name', 'name');
  });
});

TL; DR

Это очень длинный вопрос , но в большинстве случаев это примеры кода. Вопрос в том, как выполнить модульное тестирование глобальной шины событий, созданной как Vue свойство экземпляра?

В частности, у меня возникла проблема с пониманием третьего теста в tests/unit/HelloWorld.spec.js. Как проверить, что метод вызывается при отправке события? Должны ли мы вообще проверять это поведение в модульных тестах?

1 Ответ

2 голосов
/ 25 февраля 2020
  1. В тесте, когда вы проверяете, правильно ли запущен прослушиватель vm.$eventBus.$off, вы должны принудительно уничтожить компонент.
  2. В тесте метода изменения имени я добавил несколько улучшений:
    • Я прошел localVue с плагином, который инициализировал eventHub
    • Я удалил eventHub mocks, так как они больше не нужны допустимо здесь
    • Я высмеял changeName метод в настройке компонента, а не после того, как компонент создан

Вот мое предложение для tests\unit\HelloWorld.spec.js:

import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vue from 'vue';
import HelloWorld from '@/components/HelloWorld.vue';

const GlobalPlugins = {
  install(v) {
    v.prototype.$eventBus = new Vue();
  },
};

const localVue = createLocalVue();
localVue.use(GlobalPlugins);

describe('HelloWorld.vue', () => {
  const mocks = {
    $eventBus: {
      $on: jest.fn(),
      $off: jest.fn(),
      $emit: jest.fn(),
    },
  };

  it('listens to event change-name', () => {
    const wrapper = shallowMount(HelloWorld, {
      mocks,
    });
    expect(wrapper.vm.$eventBus.$on).toHaveBeenCalledTimes(1);
    expect(wrapper.vm.$eventBus.$on).toHaveBeenCalledWith('change-name', wrapper.vm.changeName);
  });

  it('removes event listener for change-name', () => {
    const wrapper = shallowMount(HelloWorld, {
      mocks,
    });

    wrapper.destroy();
    expect(wrapper.vm.$eventBus.$off).toHaveBeenCalledTimes(1);
    expect(wrapper.vm.$eventBus.$off).toHaveBeenCalledWith('change-name');
  });

  it('calls method changeName on event change-name', () => {
    const changeNameSpy = jest.fn();
    const wrapper = shallowMount(HelloWorld, {
      localVue,
      methods: {
        changeName: changeNameSpy,
      }
    });

    wrapper.vm.$eventBus.$emit('change-name', 'name');

    expect(changeNameSpy).toHaveBeenCalled();
    expect(changeNameSpy).toHaveBeenCalledWith('name');
  });
});
...