How to test eventHub or eventBus with Jest and VueJS

November 15, 2019
Testing
2 min read

Recently I had the opportunity to start integrating unit tests in some of my Vue components. The problem is I often use $eventHubs as a way to communicate between components in mid-sized applications. So for example if a component wants to notify the header of the app that something happened I normally use an eventHubto make that communication.

In my main.js file I initialize the $eventHub like this:

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

Then in the component where I want to emit the event I do this:

methods: {
  notifyHeader(){
    this.$eventHub.$emit('new_message');
  }
}

And in the component where I want to receive the events I do something like this:

created(){
  this.$eventHub.$on('new_message', () => {
    console.log('received a new message');
    // this.handleMessageMethod();
  });
}

That's how I personally go about communicating events between Vue components and it has given me great results over time. The problem came when I wanted to use Jest to test the components that were emmiting or receiving these events. Upon running npm run test I would receive the following error:

TypeError: Cannot read property '$on' of undefined

      58 |         },
      59 |         created(){
    > 60 |             this.$eventHub.$on('new_message',() => {

How to test an eventHub with VueTestUtils and Jest

To solve this you need to create your test spec file with some considerations in mind:

First, you need to remember that in your Vue code you are creating the eventHub as a variable in your Vue instance Vue.prototype.$eventHub = new Vue(). In order for the test suite to be able to find the hub in the same place we are going to use a localVue instance to replicate the hub in localVue.prototype.$eventHub = createLocalVue().

Second, we are going to use a GlobalPlugins object that will help us initialize the hub correctly on the test suite.

import { mount, createLocalVue } from '@vue/test-utils'
import Vue from 'vue';
import TestComponent from '../../src/TestComponent.vue'

const EventBus = new Vue();
const GlobalPlugins = {
    install(v) {
        // Event bus
        v.prototype.$eventHub = EventBus;
    },
};

const localVue = createLocalVue();
localVue.prototype.$eventHub = createLocalVue();
localVue.use(GlobalPlugins);

describe('TestComponent', () => {
    test('is a Vue instance', () => {
        const wrapper = mount(TestComponent, { localVue })
        expect(wrapper.isVueInstance()).toBeTruthy()
    })

})

And that's it now your test pass with an $eventHub or $eventBus:

> vue-cli-service test:unit

 PASS  tests/unit/_TestComponent.spec.js

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.853s, estimated 2s
Ran all test suites.

© Copyright 2020. The Vue Guy