12

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

// 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 测试全局事件总线,或者我应该使用另一种方法?

4

5 回答 5

8

如果组件使用全局 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
});
于 2018-08-07T08:35:12.293 回答
2

出色地,

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

不会调用您的规范文件中的此代码,因为已安装的wrp组件未使用您在上面的规范文件中导入的相同 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);

然后在您的单元测试中,您可以断言 stubbedModules['../EvtBus.js'].$on 在 code.trigger('input'); 时是否已被调用

PS:我没用过vue-test-utils。所以我不知道如何使用这个 npm 包进行存根。

但是您需要做的主要事情是找到一种方法来存根 CouponCode 组件中的 EvtBus 依赖项,以便您可以对其应用间谍并检查该间谍是否已被调用。

于 2018-01-28T13:45:59.933 回答
2

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

不,这toBeTruthy是一个功能 - 你需要(). expect(wrp.emitted('applied')).toBeTruthy实际上并没有通过,因为您需要()- 目前,它实际上什么也没做 - 没有做出任何断言。

你的断言应该是这样的:

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

您可以更进一步,并通过执行类似expect(wrp.emitted().applied.length).toBe(1).

然后,您也可以单独进行测试InputBus。如果您可以发布该组件的代码,我们可以研究如何对其进行测试。

我最近开发了一个大型 Vue 应用程序,并为主要的 repo 和文档做出了很多贡献,所以我很乐意尽我所能提供帮助。

让我知道这是否有帮助或您需要更多指导。如果可能,也发布 EventBus.vue。

于 2018-01-28T15:01:48.057 回答
1

我在 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);
于 2018-10-05T19:31:24.963 回答
1
jest.mock('@/main', () => ({
  $emit: jest.fn(),
}));

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

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

于 2020-02-04T15:23:57.657 回答