Vue如何使用插槽和插槽属性测试组件

时间:2019-06-07 10:14:22

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

我要测试此FooComponent

<div>
  <slot :fn="internalFn" />
</div>

就这样使用(ParentComponent

<FooComponent>
  <template slot-scope="slotProps">
    <BarComponent @some-event="slotProps.fn" />
  </template>
</FooComponent>

因此,我想测试组件从插槽道具调用此“ fn”时的反应。我看到的最简单的方法是采用方法本身并调用它,就像这样:

cosnt wrapper = shallowMount(FooComponent, /* ... */)
wrapper.vm.methods.internalFn(/* test payload */)
expect(wrapper.emitted()).toBe(/* some expectation */)

但这是众所周知的有关测试内部实现的反模式。因此,我想通过传递到插槽中的道具fn对其进行测试,因为它也是某种组件接口,例如组件自己的道具。

但是如何测试插槽中传递的道具呢?我可以想象它只有在测试ParentComponent这样的情况下才起作用:

const wrapper = shallowMount(ParentComponent, /* ... */)
const foo = wrapper.find(FooComponent)
wrapper.find(BarComponent).vm.$emit('some-event', /*...*/)
/* write expectations against foo */

但这就像FooComponent的内部测试ParentComponent

也许有更好的方法吗?

3 个答案:

答案 0 :(得分:1)

我参加聚会有点晚了,但我遇到了同样的问题。我从实际的Vue测试中获得了一些提示,尽管与我们相比,它们在测试中更抽象,但这确实有所帮助。

这是我想出的:

import { shallowMount } from '@vue/test-utils';
import Component from 'xyz/Component';

let wrapperSlotted;

describe('xyz/Component.vue', () => {
  beforeAll(() => {
    wrapperSlotted = shallowMount(Component, {
      data() {
        return { myProp: 'small' }
      },
      scopedSlots: {
        default: function (props) {
          return this.$createElement('div', [props.myProp]);
        }
      },
    });
  });

  it('slot prop passed to a scoped slot', () => {
    let text = wrapperSlotted.find('div').text();
    expect(text).toBe('small'); // the value of `myProp`, which has been set as the text node in the slotted <div />
  });
});

所以主要的事情是我为scopedSlots使用了render函数。

希望能帮助到某人:)

答案 1 :(得分:0)

由于没有答案,因此我只分享最终的结果。

我决定测试内部方法。这不是很酷,但是至少因为我使用打字稿,所以我有一个类型安全的测试,如果我重命名或修改要测试的方法,它将失败,并显示明确的类型错误。看起来像这样:

import Foo from '@/components/foo/Foo.ts'
import FooComponent from '@/components/foo/Foo.vue'

/*...*/

cosnt wrapper = <Foo>shallowMount(FooComponent, /* ... */)

// notice that because I passed `Foo` which is a class-component, 
// I have autocomplete and type checks for vm.*
wrapper.vm.internalFn(/* test payload */)

expect(wrapper.emitted()).toBe(/* some expectation */)

答案 2 :(得分:0)

经过一些实验,我找到了一种方法来测试在作用域槽中传递的函数。

// MyComponent.vue
<template>
  <div>
    <slot :data="{ isLoading, response, reload: fetchData }" />
  </div>
</template>

<script>
export default {
  name: 'MyComponent',
  data() {
    return {
      isLoading: false,
      response: null,
    }
  },
  created() {
    this.fetchData()
  },
  methods: {
    async fetchData() {
      this.isLoading = true
      // Some async action...
      this.response = 'Hello Word'
      this.isLoading = false
    }
  }
}
</script>


// MyComponent.spec.js
// I'm cutting some corners here, you should mock your async implementation using Jest (jest.fn() or jest.mock())
// I recommend using `flushPromises` package to flush async requests

//...
it('should fetch data again', async () => {
  const wrapper = shallowMount(MyComponent, {
    scopedSlots: {
      default: `
        <test @click="props.data.reload">Click here</test>
      `,
    },
    components: {
      test: Vue.component('test', {
        template: `<div><slot /></div>`
      })
    },
  })

  await wrapper.vm.$nextTick()
  // await flushPromises()

  /* Pre-assertion */
  expect(myMock).toHaveBeenCalledTimes(1)

  wrapper.find('test-stub').vm.$emit('click')

  await wrapper.vm.$nextTick()
  // await flushPromises()

  /* Assertion */
  expect(myMock).toHaveBeenCalledTimes(2)
})