单元测试依赖全局事件总线的 Vue 组件

Unit testing Vue components that rely on a global event bus

我在全局 app.js 中声明了一个事件总线,如下所示:

window.Event = new Vue();

组件看起来像

export default {
    data() {
        return {
            hasError: false,
            zip: '',
        };
    },

    methods: {
        setZip: function() {
            this.hasError = false;
            this.$emit('setZip', this.zip);
        },
    },

    mounted() {
        Event.$on('showErrors', (errors) => {
            this.hasError = errors.zip ? true : false;
        });

        this.zip = this.editZip;
    },

    props: [
        'editZip'
    ],
}

我用 ava 和以下 helpers/setup.js:

对我的组件进行单元测试
const browserEnv = require('browser-env');
const hook = require('vue-node');
const { join } = require('path');

// Setup a fake browser environment
browserEnv();

// Pass an absolute path to your webpack configuration to the hook function.
hook(join(__dirname, './webpack.config.js'));

webpack.config.js 看起来像:

module.exports = {
    module: {
      loaders: [
          {
            test: /\.vue$/,
            loader: 'vue-loader',
          },
        {
          test: /\.js$/,
            loader: 'babel',
            exclude: /node_modules/,
          },
      ],
    },
  resolve: {
      extensions: ['.js', '.vue'],
      },
};

当运行下面测试

import Vue from 'vue/dist/vue.js';
import test from 'ava';
import Zip from '../../resources/assets/js/components/Company/Zip.vue';

let vm;

test.beforeEach(t => {
    let Z = Vue.extend(Zip);

    vm = new Z({ propsData: {
        editZip: 1220
    }}).$mount();
});

test('that it renders a div with class form-group', t => {
    t.is(vm.$el.className, 'form-group');
});

它通过了,但抛出了以下错误:

[Vue warn]: Error in mounted hook: "TypeError: Event.$on is not a function"

(found in <Root>)
TypeError: Event.$on is not a function
    at VueComponent.mounted (/mnt/c/code/leaflets/resources/assets/js/components/Company/City.vue:107:15)
    at callHook (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:2530:21)
    at mountComponent (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:2424:5)
    at VueComponent.Vue.$mount (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:7512:10)
    at VueComponent.Vue.$mount (/mnt/c/code/leaflets/node_modules/vue/dist/vue.js:9592:16)
    at Test._ava2.default.beforeEach.t [as fn] (/mnt/c/code/leaflets/tests/js/CompanyCity.js:12:9)
    at Test.callFn (/mnt/c/code/leaflets/node_modules/ava/lib/test.js:281:18)
    at Test.run (/mnt/c/code/leaflets/node_modules/ava/lib/test.js:294:23)
    at runNext (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:58:44)
    at Sequence.run (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:90:10)
    at Concurrent.run (/mnt/c/code/leaflets/node_modules/ava/lib/concurrent.js:41:37)
    at runNext (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:58:44)
    at Sequence.run (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:90:10)
    at runNext (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:58:44)
    at Sequence.run (/mnt/c/code/leaflets/node_modules/ava/lib/sequence.js:90:10)
    at Bluebird.try (/mnt/c/code/leaflets/node_modules/ava/lib/runner.js:214:48)
    at tryCatcher (/mnt/c/code/leaflets/node_modules/bluebird/js/release/util.js:16:23)
    at Function.Promise.attempt.Promise.try (/mnt/c/code/leaflets/node_modules/bluebird/js/release/method.js:39:29)
    at Runner.run (/mnt/c/code/leaflets/node_modules/ava/lib/runner.js:214:22)
    at process.on.options (/mnt/c/code/leaflets/node_modules/ava/lib/main.js:82:10)
    at emitOne (events.js:96:13)
    at process.emit (events.js:191:7)
    at process.on.message (/mnt/c/code/leaflets/node_modules/ava/lib/process-adapter.js:14:10)
    at emitTwo (events.js:106:13)
    at process.emit (events.js:194:7)
    at process.nextTick (internal/child_process.js:766:12)
    at _combinedTickCallback (internal/process/next_tick.js:73:7)
    at process._tickCallback (internal/process/next_tick.js:104:9)

我不知道在我的测试中在哪里声明window.Event = new Vue(),以便被测试的组件可以访问Event变量。

首先,我建议从 window 对象中删除全局 EventBus(如果可以的话),因为依赖可能被其他部分覆盖的全局 props 通常并不理想导致不良影响的代码。

您可以只创建一个 event-bus.js 文件并在其中创建事件总线:

import Vue from 'vue';
export const EventBus = new Vue();

然后您可以从需要访问事件总线的任何其他组件导入该文件,例如

import {EventBus} from '/path/to/event-bus.js'

从那里,您可以完全按照当前使用的方式使用它。

但是,如果您 出于某种原因将 EventBus 保留在 window 对象中,则您还必须模拟它以便对其进行测试。

首先,当您调用 browserEnv 函数时,您需要 mock the window object 这样做:

browserEnv(['window']);

然后,window 将作为全局变量可用,但您需要更改组件的实现以明确地从 window 对象中获取 EventBus,例如:

window.EventBus

您也可以在您的组件中全局定义它,例如:

const { EventBus } = window

让我知道这是否可行。

谢谢,