[英]How to test a global event bus in VueJS
In this article it is explained how to use a global event bus in VueJS. 本文解释了如何在 VueJS 中使用全局事件总线。 It describes an alternative method to the common approach using an event bus defined in a separate file:
它描述了使用在单独文件中定义的事件总线的通用方法的替代方法:
import Vue from 'vue';
const EventBus = new Vue();
export default EventBus;
This has to be imported in every SFC where it is needed.这必须在需要它的每个 SFC 中导入。 The alternative approach attaches the global event bus to the main Vue instance:
另一种方法是将全局事件总线附加到主 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');
Now I have the problem that I don't know how to use the global event bus created in this way in the unit tests.现在我有一个问题,我不知道如何在单元测试中使用以这种方式创建的全局事件总线。
There is already a question about testing the global event bus using the first mentioned approach, but no answer is accepted.已经有一个关于使用第一个提到的方法测试全局事件总线的问题,但没有答案被接受。
I tried as suggested in one of the answers to use createLocalVue
, but that didn't help:我按照使用
createLocalVue
的答案之一中的建议进行了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);
});
This says expected 0, actual 1. I tried with async
function and $nextTick()
but without success.这表示预期为 0,实际为 1。我尝试使用
async
函数和$nextTick()
但没有成功。
For the previous example I'm using mocha
, chai
and sinon
.对于前面的示例,我使用的是
mocha
、 chai
和sinon
。 This is just for illustration.这只是为了说明。 Answers using
jest
or any other testing framework / assertion library are highly appreciated.非常感谢使用
jest
或任何其他测试框架/断言库的答案。
Reading the book "Testing Vue.js Applications" from the Edd Yerburgh, author of @vue/test-utils
, I came up with some ideas, but I'm still struggling to understand how to accomplish the testing of the global event bus added as an instance property.阅读
@vue/test-utils
作者 Edd Yerburgh 的“Testing Vue.js Applications”一书,我提出了一些想法,但我仍然在努力理解如何完成对添加的全局事件总线的测试作为实例属性。 In the book instance properties are mocked in the unit tests.在书中实例属性在单元测试中被模拟。
I created a git repository with example code following the article from medium.com .我按照来自medium.com的文章创建了一个带有示例代码的git 存储库。 For this example I used
jest
for unit testing.在这个例子中,我使用
jest
进行单元测试。
This is the code:这是代码:
src/main.js
import Vue from 'vue';
import App from './App.vue';
// create global event bus as instance property Vue.prototype.$eventBus = new Vue();
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
Change name src/components/ChangeName.vue
更改名称
<script>
export default {
name: 'ChangeName',
data() {
return {
newName: '',
};
},
methods: {
changeName() {
this.$eventBus.$emit('change-name', this.newName);
},
},
};
</script>
It's a very simple application with two components.这是一个非常简单的应用程序,包含两个组件。 The component
ChangeName.vue
has an input element and the user can trigger a method by clicking a button.组件
ChangeName.vue
有一个 input 元素,用户可以通过点击一个按钮来触发一个方法。 The method emits an event change-name
using the global event bus.该方法使用全局事件总线发出事件
change-name
。 The component HelloWorld.vue
listens to the event change-name
and updates the model property name
.组件
HelloWorld.vue
监听事件change-name
并更新模型属性name
。
Here is how I have tried to test it:这是我尝试测试它的方法:
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', () => {
// this test passes
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', () => {
// this test does not pass
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', () => {
// this test does not pass
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', () => {
// this test passes
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 TL; 博士
It is a very long question, but most of it are code examples.这是一个很长的问题,但大部分都是代码示例。 The question is how to unit test a global event bus created as Vue instance property?
问题是如何对创建为 Vue 实例属性的全局事件总线进行单元测试?
In particular I have a problem understanding the third test in tests/unit/HelloWorld.spec.js
.特别是我在理解
tests/unit/HelloWorld.spec.js
的第三个测试时遇到问题。 How do I check that the method is called when an event is emitted?如何检查在发出事件时是否调用了该方法? Should we test this behaviour in unit tests at all?
我们是否应该在单元测试中测试这种行为?
vm.$eventBus.$off
listener was fired properly, you have to force component to destroy.vm.$eventBus.$off
侦听器是否正确触发的测试中,您必须强制组件销毁。localVue
with plugin that initialized eventHublocalVue
带有初始化 eventHub 的插件的 localVueeventHub
mocks as they are no longer valid hereeventHub
因为它们在这里不再有效changeName
method in component setup, not after a component is createdchangeName
方法,而不是在创建组件之后Here is my suggestion for tests\\unit\\HelloWorld.spec.js
:这是我对
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');
});
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.