title: Mocking custom modules in VueJS components using Jest
body: <p>I'm trying to write a simple Jest unit test on one of my Vue components.</p>
  
  <p>Tech stack: TypeScript, VueJS, Jest, Webpack,</p>
  
  <p><strong>Structure</strong>:</p>
  
  <pre><code> root
     - src
       - invite
        - accept.ts
        - accept.vue
     - test
       - invite
         - accept.ts
     - app.ts
     - jest.config.json
     - package.json
  </code></pre>
  
  <p>I've been observing some strange behaviour when attempting to mock out a dependency module using Jest.</p>
  
  <p><strong>src/invite/accept.ts</strong>:</p>
  
  <pre><code>import Vue from "vue";
  import Component from 'vue-class-component';
  import { eventBus } from './../app';
  
  @Component
  export default class InviteAccept extends Vue {
  
      created() {
          console.log(eventBus);
          eventBus.$emit('hideNavigation');
      };
  }
  </code></pre>
  
  <p><strong>src/invite/accept.vue</strong></p>
  
  <pre><code>&lt;template&gt;
      &lt;div class="row"&gt;
          &lt;div class="col"&gt;
              &lt;h1&gt;Blah&lt;/h1&gt;
          &lt;/div&gt;
      &lt;/div&gt;
  &lt;/template&gt;
  &lt;script src="./accept.ts" lang="ts"&gt;&lt;/script&gt;
  </code></pre>
  
  <p><strong>src/app.ts</strong></p>
  
  <pre><code>import { polyfill } from 'es6-promise'
  import Vue from 'vue';
  import VueRouter from 'vue-router';
  import Invite from './invite/accept.vue';
  
  polyfill();
  
  export const eventBus = new Vue();
  
  const router = new VueRouter({
      routes: [
          { path: '/invite/accept/:token', component: Invite, name: 'inviteAccept' },
          { path: '/', component: undefined, name: 'index' },
      ]
  });
  
  Vue.use(VueRouter);
  
  const app = new Vue({ router }).$mount('#app');
  </code></pre>
  
  <p><strong>/jest.config.json</strong></p>
  
  <pre><code>{
    "transform": {
      "\\.(ts|tsx)$": "&lt;rootDir&gt;/node_modules/ts-jest/preprocessor.js",
      "\\.(vue)$": "&lt;rootDir&gt;/node_modules/vue-jest"
    },
    "testRegex": "/test/.*\\.(ts|tsx)$",
    "moduleFileExtensions": [
      "vue",
      "ts",
      "tsx",
      "js"
    ],
    "transformIgnorePatterns": [
      "&lt;rootDir&gt;/node_modules/(?!lodash-es/.*)"
    ]
  }
  </code></pre>
  
  <p><strong>/package.json</strong></p>
  
  <pre><code>{
    "name": "blah",
    "version": "1.0.0",
    "license": "UNLICENSED",
    "scripts": {
      "build": "webpack --config webpack.prod.js",
      "watch": "webpack --watch --config webpack.dev.js",
      "test": "jest -c jest.config.json"
    },
    "devDependencies": {
      "@types/es6-promise": "^3.3.0",
      "@types/jest": "^22.2.3",
      "@vue/test-utils": "^1.0.0-beta.16",
      "applicationinsights-js": "^1.0.15",
      "bootstrap-vue": "^2.0.0-rc.9",
      "clean-webpack-plugin": "^0.1.19",
      "css-loader": "^0.28.10",
      "es6-promise": "^4.2.4",
      "extract-text-webpack-plugin": "3.0.2",
      "html-webpack-plugin": "^3.0.6",
      "jest": "^22.4.3",
      "jest-teamcity-reporter": "^0.9.0",
      "ts-jest": "^22.4.6",
      "ts-loader": "3.4.0",
      "typescript": "^2.7.2",
      "vue": "^2.5.13",
      "vue-class-component": "^6.2.0",
      "vue-jest": "^2.5.0",
      "vue-loader": "^14.1.1",
      "vue-router": "^3.0.1",
      "vue-style-loader": "^4.0.2",
      "vue-template-compiler": "^2.5.13",
      "vue-types": "^1.2.0",
      "webpack": "3.10.0",
      "webpack-merge": "^4.1.2"
    },
    "dependencies": {}
  }
  </code></pre>
  
  <p>And finally, the test class</p>
  
  <p><strong>/test/invite/accept.ts</strong></p>
  
  <pre><code>import InviteAccept from './../../src/invite/accept';
  import { eventBus } from './../../src/app';
  import { shallowMount, createLocalVue } from '@vue/test-utils';
  import VueRouter = require('vue-router'); // This needs to be 'require', rather than 'from'. Some weird thing or something.
  import 'jest';
  
  describe('invites', () =&gt; {
      jest.mock('./../../src/app', () =&gt; 'anything');
  
      function createWrapper() {
          const localVue = createLocalVue();
          localVue.use(VueRouter);
          const router = new VueRouter();
  
          return shallowMount(InviteAccept, {
              localVue,
              router
          });
      };
  
      test('should mock in test', () =&gt; {
          // this works:
          const test = require('./../../src/app');
          expect(test).toEqual('anything');
      });
  
      test('should mock in component', () =&gt; {
          const wrapper = createWrapper();
      });
  
  });
  </code></pre>
  
  <p>The test called <code>should mock in test</code> gets the mocked value for <code>eventBus</code> and passes.</p>
  
  <p>The test called <code>should mock in component</code> does not get the mocked value for <code>eventBus</code> and it is <code>undefined</code>. The exact error is as follows:</p>
  
  <pre><code>TypeError: Cannot read property '$emit' of undefined
  
        at VueComponent.Object.&lt;anonymous&gt;.InviteAccept.created (src/invite/accept.vue:49:23)
        at callHook (node_modules/vue/dist/vue.runtime.common.js:2919:21)
        at VueComponent.Vue._init (node_modules/vue/dist/vue.runtime.common.js:4628:5)
        at new VueComponent (node_modules/vue/dist/vue.runtime.common.js:4796:12)
        at createInstance (node_modules/@vue/test-utils/dist/vue-test-utils.js:4230:12)
        at mount (node_modules/@vue/test-utils/dist/vue-test-utils.js:5376:12)
        at Object.shallowMount (node_modules/@vue/test-utils/dist/vue-test-utils.js:5414:10)
        at createWrapper (test/invite/accept.ts:13:29)
        at Object.&lt;anonymous&gt; (test/invite/accept.ts:25:23)
  </code></pre>
  
  <p>The <code>console.log(eventBus);</code> inside the component's <code>created()</code> function also yields <code>undefined</code>.</p>
  
  <p>If I try and <code>console.log(eventBus);</code> inside the test, it is <code>undefined</code> too, which i just don't understand either.</p>
  
  <p>What I expect to happen is that the <code>eventBus</code> instance inside the component is replaced. I realise that the mock will never work whilst set to 'Anything' but I expect to see it set to the mocked value at least.</p>
  
  <p>According to the Jest documentation (<a href="https://facebook.github.io/jest/docs/en/es6-class-mocks.html" rel="nofollow noreferrer">https://facebook.github.io/jest/docs/en/es6-class-mocks.html</a>) on Automatic Mocking I think the above is correct.</p>
  
  <p>I'm sure I'm missing something obvious but I have no idea what it is. Any help would be greatly appreciated :-)</p>