admin管理员组文章数量:1422254
In this article it is explained how to use a global event bus in VueJS. It describes an alternative method to the mon 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. The alternative approach attaches the global event bus to the main Vue instance:
// 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:
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.
For the previous example I'm using mocha
, chai
and sinon
. This is just for illustration. Answers using jest
or any other testing framework / assertion library are highly appreciated.
EDIT on 25th February 2020
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 acplish the testing of the global event bus added as an instance property. In the book instance properties are mocked in the unit tests.
I created a git repository with example code following the article from medium. For this example I used jest
for unit testing.
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 './ponents/HelloWorld.vue';
import ChangeName from './ponents/ChangeName.vue';
export default {
name: 'App',
ponents: {
HelloWorld,
ChangeName,
},
};
</script>
src/ponents/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/ponents/ChangeName.vue
Change name
<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 ponents. The ponent ChangeName.vue
has an input element and the user can trigger a method by clicking a button. The method emits an event change-name
using the global event bus. The ponent HelloWorld.vue
listens to the event change-name
and updates the model property name
.
Here is how I have tried to test it:
tests\unit\HelloWorld.spec.js
import { shallowMount } from '@vue/test-utils';
import HelloWorld from '@/ponents/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 '@/ponents/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
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?
In particular I have a problem understanding the third test in 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?
In this article it is explained how to use a global event bus in VueJS. It describes an alternative method to the mon 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. The alternative approach attaches the global event bus to the main Vue instance:
// 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:
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.
For the previous example I'm using mocha
, chai
and sinon
. This is just for illustration. Answers using jest
or any other testing framework / assertion library are highly appreciated.
EDIT on 25th February 2020
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 acplish the testing of the global event bus added as an instance property. In the book instance properties are mocked in the unit tests.
I created a git repository with example code following the article from medium.. For this example I used jest
for unit testing.
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 './ponents/HelloWorld.vue';
import ChangeName from './ponents/ChangeName.vue';
export default {
name: 'App',
ponents: {
HelloWorld,
ChangeName,
},
};
</script>
src/ponents/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/ponents/ChangeName.vue
Change name
<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 ponents. The ponent ChangeName.vue
has an input element and the user can trigger a method by clicking a button. The method emits an event change-name
using the global event bus. The ponent HelloWorld.vue
listens to the event change-name
and updates the model property name
.
Here is how I have tried to test it:
tests\unit\HelloWorld.spec.js
import { shallowMount } from '@vue/test-utils';
import HelloWorld from '@/ponents/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 '@/ponents/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
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?
In particular I have a problem understanding the third test in 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?
1 Answer
Reset to default 3 +50- In test where you are checking if
vm.$eventBus.$off
listener was fired properly, you have to force ponent to destroy. - In change name method test I added a few improvements:
- I passed
localVue
with plugin that initialized eventHub - I removed
eventHub
mocks as they are no longer valid here - I mocked
changeName
method in ponent setup, not after a ponent is created
- I passed
Here is my suggestion for tests\unit\HelloWorld.spec.js
:
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vue from 'vue';
import HelloWorld from '@/ponents/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');
});
});
本文标签: javascriptHow to test a global event bus in VueJSStack Overflow
版权声明:本文标题:javascript - How to test a global event bus in VueJS - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745364643a2655455.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论