Vue 如何使用 slot 和 slot-props 测试组件
Vue how to test component with slot and slot-props
我想测试这个FooComponent
:
<div>
<slot :fn="internalFn" />
</div>
它是这样使用的(例如在ParentComponent
中):
<FooComponent>
<template slot-scope="slotProps">
<BarComponent @some-event="slotProps.fn" />
</template>
</FooComponent>
所以我想测试我的组件在从 slot 道具调用这个 "fn" 时如何反应。我看到的最简单的方法是获取方法本身并调用它,就像这样:
cosnt wrapper = shallowMount(FooComponent, /* ... */)
wrapper.vm.methods.internalFn(/* test payload */)
expect(wrapper.emitted()).toBe(/* some expectation */)
但这是众所周知的关于测试内部实现的反模式。所以我想通过传递到插槽中的 prop fn
来测试它,因为它也是某种组件接口,比如组件自己的 props。
但是如何测试插槽中传递的道具?我可以想象它只有在我测试 ParentComponent
类似的东西时才会起作用:
const wrapper = shallowMount(ParentComponent, /* ... */)
const foo = wrapper.find(FooComponent)
wrapper.find(BarComponent).vm.$emit('some-event', /*...*/)
/* write expectations against foo */
但这感觉就像 FooComponent
的测试 ParentComponent
的内部测试
也许有更好的方法?
UPD: 线下是我多年前给出的原始答案。对于今天,我会说这些方法之一就足够了:
不测试插槽方法,而是测试依赖于它的user-facing特性。首先尝试手动测试它,注意你是怎么做的,然后尝试编写一个类似的测试。例如。 testing-library/vue
如果第一个选项太难做,试着想出一些假的测试组件。这个想法和我在问题中描述的非常相似
But that feels like tests for FooComponent inside tests for ParentComponent
但是不使用 ParentComponent
而是创建一些非常简单的内联组件(就在您的 FooComponent.spec.js
文件中),它使用带有插槽的组件。
2020年给出的原答案:
因为没有答案所以我只分享我的结果。
我决定测试内部方法。这并不酷,但至少,因为我使用打字稿,所以我有一个 type-safe 测试,如果我重命名或修改我测试的方法,它将失败并出现明显的类型错误。看起来像这样:
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 */)
我来晚了一点,但我遇到了同样的问题。我从实际的 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
的渲染函数。
希望对某人有所帮助:)
我想测试这个FooComponent
:
<div>
<slot :fn="internalFn" />
</div>
它是这样使用的(例如在ParentComponent
中):
<FooComponent>
<template slot-scope="slotProps">
<BarComponent @some-event="slotProps.fn" />
</template>
</FooComponent>
所以我想测试我的组件在从 slot 道具调用这个 "fn" 时如何反应。我看到的最简单的方法是获取方法本身并调用它,就像这样:
cosnt wrapper = shallowMount(FooComponent, /* ... */)
wrapper.vm.methods.internalFn(/* test payload */)
expect(wrapper.emitted()).toBe(/* some expectation */)
但这是众所周知的关于测试内部实现的反模式。所以我想通过传递到插槽中的 prop fn
来测试它,因为它也是某种组件接口,比如组件自己的 props。
但是如何测试插槽中传递的道具?我可以想象它只有在我测试 ParentComponent
类似的东西时才会起作用:
const wrapper = shallowMount(ParentComponent, /* ... */)
const foo = wrapper.find(FooComponent)
wrapper.find(BarComponent).vm.$emit('some-event', /*...*/)
/* write expectations against foo */
但这感觉就像 FooComponent
的测试 ParentComponent
也许有更好的方法?
UPD: 线下是我多年前给出的原始答案。对于今天,我会说这些方法之一就足够了:
不测试插槽方法,而是测试依赖于它的user-facing特性。首先尝试手动测试它,注意你是怎么做的,然后尝试编写一个类似的测试。例如。 testing-library/vue
如果第一个选项太难做,试着想出一些假的测试组件。这个想法和我在问题中描述的非常相似
But that feels like tests for FooComponent inside tests for ParentComponent
但是不使用 ParentComponent
而是创建一些非常简单的内联组件(就在您的 FooComponent.spec.js
文件中),它使用带有插槽的组件。
2020年给出的原答案:
因为没有答案所以我只分享我的结果。
我决定测试内部方法。这并不酷,但至少,因为我使用打字稿,所以我有一个 type-safe 测试,如果我重命名或修改我测试的方法,它将失败并出现明显的类型错误。看起来像这样:
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 */)
我来晚了一点,但我遇到了同样的问题。我从实际的 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
的渲染函数。
希望对某人有所帮助:)