如何使用Vue Test Utils测试全局事件总线?

时间:2018-01-28 12:22:17

标签: unit-testing vue.js vue-test-utils

我正在尝试学习如何测试通过全局事件总线发出的事件。这里的代码在我不知道该做什么的地方有一些评论。

// EvtBus.js
import Vue from 'vue';
export const EvtBus = new Vue();
<!-- CouponCode.vue -->
<template>
    <div>
        <input
            class="coupon-code"
            type="text"
            v-model="code"
            @input="validate">
        <p v-if="valid">
            Coupon Redeemed: {{ message }}
        </p>
    </div>
</template>

<script>

import { EvtBus } from '../EvtBus.js';

export default {
    data () {
        return {
            code: '',
            valid: false,

            coupons: [
                {
                    code: '50OFF',
                    discount: 50,
                    message: '50% Off!'
                },
                {
                    code: 'FREE',
                    discount: 100,
                    message: 'Entirely Free!'
                }
            ]
        };
    },

    created () {
        EvtBus.$on('coupon-applied', () => {
            //console.info('had a coupon applied event on component');
        });
    },

    methods: {
        validate () {
            // Extract the coupon codes into an array and check if that array
            // includes the typed in coupon code.
            this.valid = this.coupons.map(coupon => coupon.code).includes(this.code);
            if (this.valid) {
                this.$emit('applied');
                // I NEVER see this on the coupon-code.spec.js
                EvtBus.$emit('coupon-applied');
            }
        }
    },

    computed: {
        message () {
            return this.coupons.find(coupon => coupon.code === this.code).message;
        }
    }
}
</script>
// tests/coupon-code.spec.js
import expect from 'expect';
import { mount } from '@vue/test-utils';
import CouponCode from '../src/components/CouponCode.vue';
import { EvtBus } from '../src/EvtBus.js';

describe('Reminders', () => {
    let wrp;

    beforeEach(() => {
        wrp = mount(CouponCode);
    });

    it('broadcasts the percentage discount when a valid coupon code is applied', () => {
        let code = wrp.find('input.coupon-code');
        code.element.value = '50OFF';
        code.trigger('input');

        console.log(wrp.emitted('applied'));

        //
        // I NEVER see this on the outpout.
        // How can I test it through a global event bus rather than
        // an event emitted from the component instance?
        //
        EvtBus.$on('coupon-applied', () => {
            console.log('coupon was applied through event bus');
        });

        // Passes, but not using EvtBus instance.
        expect(wrp.emitted('applied')).toBeTruthy;

    });
});

所以,我怀疑的是如何测试全局事件总线是否正在发出和监听使用该事件总线的组件内的事件。

那么,是否可以使用Vue Test Utils测试全局事件总线,或者我应该使用其他方法?

5 个答案:

答案 0 :(得分:3)

如果组件正在使用全局EventBus,例如,将其导入到给定组件之外并分配给window.EventBus,则可以使用全局Vue实例将$ on或$ emit事件重定向到包装器的vm实例。这样,您就可以继续编写测试,就好像组件是通过this.$emit而不是EventBus.$emit发出一样:

it('clicking "Settings" button emits "openSettings"', () => {
    global.EventBus = new Vue();
    global.EventBus.$on('openSettings', (data) => {
        wrapper.vm.$emit('openSettings', data);
    });

    // component emits `EventBus.$emit('openSettings')`

    expect(wrapper.emitted('openSettings')).toBeTruthy(); // pass
});

答案 1 :(得分:2)

那么,

EvtBus.$on('coupon-applied', () => {
    console.log('coupon was applied through event bus');
});

您的规范文件中的此代码不会被调用,因为已安装的 wrp 组件未使用您在上面的spec文件中导入的相同EvtBus。

您需要测试的是一个名为inject-loader的npm包,以便您可以提供自己的优惠券代码组件的EvtBus依赖项实现(存根)。

有点像这样

const couponCodeInjector = require('!!vue-loader?inject!src/views/CouponCode');

const stubbedModules = {
   '../EvtBus.js': {
        $on : sandbox.spy((evtName, cb) => cb()); 
    }
};

const couponCode = couponCodeInjector(stubbedModules);

然后在你的单元测试中你可以断言当code.trigger(&#39;输入&#39)时是否调用了stubbedModules [&#39; ../ EvtBus.js&#39;]。$ on );

PS:我还没有使用过vue-test-utils。因此,我不知道如何使用此npm包进行存根。

但您需要做的主要是找到一种方法来存储您在CouponCode组件中的EvtBus依赖关系,以便您可以在其上应用间谍并检查是否已调用该间谍。

答案 2 :(得分:2)

单元测试应侧重于单独测试单个组件。在这种情况下,您要测试是否发出事件,因为这是CouponCode.vue的工作。请记住,单元测试应该专注于测试最小的代码单元,并且一次只测试一件事。在这种情况下,我们关心事件的发生 - EventBus.test.js是我们测试发生事件时会发生什么的地方。

Noe toBeTruthy是一个功能 - 您需要()expect(wrp.emitted('applied')).toBeTruthy实际上传递,因为您需要() - 此刻,它实际上什么都不做 - 没有断言。

你的断言应该是什么样的:

expect(wrp.emitted('applied')).toBeTruthy()

您可以更进一步,并确保仅通过expect(wrp.emitted().applied.length).toBe(1)之类的操作发出一次。

然后,您也可以单独测试InputBus。如果您可以发布该组件的代码,我们可以尝试如何测试它。

我最近在一个大型Vue应用程序上工作,并为主要的回购和文档做出了很多贡献,所以我很乐意随时随地提供帮助。

如果有帮助或者您需要更多指导,请告诉我。如果可能,请发布EventBus.vue。

答案 3 :(得分:1)

jest.mock('@/main', () => ({
  $emit: jest.fn(),
}));

从一开始就将此包含在规范文件中的代码中。

注意:'@/main'是您要从中导入事件总线的文件。

答案 4 :(得分:0)

我对vue-test-utils和Jest遇到了同样的问题。对我来说,vue-test-utils库的createLocalVue()解决了该问题。此功能创建Vue的本地副本,以在安装组件时使用。在此Vue副本上安装插件可防止污染原始Vue副本。 (https://vue-test-utils.vuejs.org/api/options.html#localvue

将此添加到测试文件将解决此问题:

const EventBus = new Vue();

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

// create a local instance of the global bus
const localVue = createLocalVue();
localVue.use(GlobalPlugins);