如何在Vue 2中测试组件根元素上的事件?

3

我正在为以下组件编写单元测试:

<template>
  <sub-component
     @foo="bar"
  />
</template>
<script>
import SubComponent from './SubComponent';

export default {
  name: 'MyComponent',
  components: { SubComponent },
  methods: {
    bar(payload) {
       this.$emit('baz', ...payload);
    }
  }
}
</script>

测试内容如下:

import { shallowMount } from '@vue/test-utils';
import _ from 'lodash';
import MyComponent from '../../components/MyComponent';

describe('MyComponent.vue', () => {
  let wrapper;

  beforeEach(() => {
    wrapper = shallowMount(MyComponent);
  });

  it('should emit baz on subcomponent foo', () => {
    const subComp = wrapper.find('sub-component-stub');
    expect(subComp.exists()).toBe(true);          // passes

    subComp.vm.$emit('foo');

    return wrapper.vm.$nextTick().then(() => {
      expect(wrapper.emitted().baz).toBeTruthy(); // does not pass;

      // upon logging: 
      console.log(_.isEqual(wrapper, subComp));   // => true 
    })
  })
})

这个例子过于简化,但是这里的原则是我想要一个可重用的<sub-component>(一个模态框),以及各种功能包装器(与模态框执行的特定任务相关),它们映射了额外的功能。我不希望在父组件中添加功能,因为这会违反DRY - 我必须将其放置在每个包含特定类型模态框的组件中。
如果<sub-component>不是<template>的直接子级,则可以正常工作。不知何故,看起来和托管在同一个元素上。
应该如何正确地测试这个呢?
2个回答

5

另一个可能性是在DOM中找到您的元素并检查根组件发出的值。

import { shallowMount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'
import SubComponent from './SubComponent.vue'

describe('MyComponent', () => {    

  it('should emit baz on subcomponent foo', () => {
    const wrapper = shallowMount(MyComponent)
    const subComponent = wrapper.find(SubComponent)

    expect(subComponent.exists()).toBe(true)
    expect(wrapper.emitted('baz')).toBeUndefined()

    subComponent.vm.$emit('foo', ['hello'])
    expect(wrapper.emitted('baz')[0]).toEqual(['hello'])
    // or expect(wrapper).toEmit('baz', 'hello') cf. below for toEmit
  })

})

如果你想要一个自定义的 Jest 匹配器:
toEmit(received, eventName, data) {
  if (data) {
    expect(received.emitted()[eventName][0]).toEqual([data])
  } else {
    expect(received.emitted()[eventName][0]).toEqual([])
  }
  return { pass: true }
} 

在我的测试中,wrapper.find(SubComponent) 会出错,因为 SubComponent 没有定义:它没有被导入。我不是使用 mount,而是使用 shallowMount,这使得所有子组件都成为存根。正如您在问题中可能注意到的那样,我正确地找到了子组件(用于 shallowMount)。然而,由于子组件也是组件的根元素(因为它是 <template> 的唯一子元素),所以正常的 $emit('foo', payload) 不像嵌套更深的情况下那样起作用:(例如:<template><div><sub-component/></div></template> - 这个可以正常工作)。 - tao
假设我导入SubComponent并将其声明在wrapper的“stubs”属性中,你的解决方案与我的问题中的代码存在相同的问题。 除非首先使用“createWrapper()”创建SubComponent的包装器,否则不会调用wrapper的“bar()”函数。 - tao
@AndreiGheorghiu 我更新了我的答案。由于 payload 的传播,我需要在测试中为 emit 的 args 参数创建一个数组:subComponent.vm.$emit('foo', ['hello'])。对我来说,答案中的代码运行良好。请告诉我。 - mickaelw
你说得对,如果我导入SubComponent并找到它,它就能正常工作。 - tao

0
当子组件是根组件时,选择器wrapper.find(SubComponent)将引用根组件, 因此应该使用$children[0]
// subComponent here is the root which is actually MyComponent
const subComponent = wrapper.find(SubComponent); 
// subComponent.vm.$children[0] is the actual SubComponent
subComponent.vm.$children[0].$emit('foo', ['hello'])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接