四、测试属性和自定义事件
测试属性、事件和自定义事件有不同的方法。
属性是从父组件传递到子组件的自定义属性。自定义事件的作用正好相反:它们通过事件将数据发送到直接父级。当它们结合在一起时,它们是 Vue.js 组件中交互和通信的导线。
在单元测试中,测试输入和输出(属性和自定义事件)意味着测试组件在隔离地接收和发送数据时的行为。所以,让我们把手弄脏。
性质
当我们测试组件属性时,我们可以测试组件在传递某些属性时的行为。然而,在继续之前,请考虑一下这个重要的注意事项:
要将属性传递给组件,请使用propsData
而不是props
。后者用于定义属性,而不是传递数据。
首先,创建一个Message.test.js
文件并添加以下代码:
describe("Message.test.js", () => {
let cmp;
describe("Properties", () => {
// @TODO
});
});
我们将测试用例分组在一个describe
表达式中,它们可以嵌套。因此,我们可以使用此策略分别对属性和事件的测试进行分组。
然后,我们将创建一个帮助器工厂函数来创建消息组件,并为其提供一些属性:
const createCmp = propsData => mount(Message, { propsData });
测试性能是否存在
我们可以测试的最明显的事情是属性是否存在。记住,Message.vue
组件有一个message
属性,所以我们假设它正确地接收到该属性。vue test utils 带有一个hasProp(prop, value)
功能,在这种情况下非常方便:
it("has a message property", () => {
cmp = createCmp({ message: "hey" });
expect(cmp.hasProp("message", "hey")).toBeTruthy();
});
属性的行为方式是,只有在组件中声明属性时才会接收它们。这意味着如果我们传递一个未定义的属性,那么它将不会被接收。因此,要检查属性是否不存在,可以使用不存在的属性:
it("has no cat property", () => {
cmp = createCmp({ cat: "hey" });
expect(cmp.hasProp("cat", "hey")).toBeFalsy();
});
但是,在这种情况下,测试将失败,因为 Vue 具有非道具属性(https://vuejs.org/v2/guide/components.html#Non-道具属性。这将其设置为Message
组件的根,因此被识别为道具,因此测试将返回true
。将其更改为toBeTruty
将使此示例通过:
it("has no cat property", () => {
cmp = createCmp({ cat: "hey" });
expect(cmp.hasProp("cat", "hey")).toBeTruthy();
});
我们也可以测试默认值。转到Message.vue
并更改props
如下:
props: {
message: String,
author: {
type: String,
default: 'Paco'
}
},
然后,测试可以如下所示:
it("Paco is the default author", () => {
cmp = createCmp({ message: "hey" });
expect(cmp.hasProp("author", "Paco")).toBeTruthy();
});
断言属性验证
属性可以具有验证规则,以确保属性是必需的或是确定的类型。让我们将message
属性写如下:
props: {
message: {
type: String,
required: true,
validator: message => message.length > 1
}
}
更进一步,您可以使用自定义构造函数类型或自定义验证规则,如您在the documentation
(中所见 https://vuejs.org/v2/guide/components.html#Prop-验证。现在不要这样做;我只是举个例子:
class Message {}
props: {
message: {
type: Message, // It's compared using instance of
...
}
}
}
每当未满足验证规则时,Vue 将显示一个console.error
。例如,对于createCmp({ message: 1 })
,错误如下:
[Vue warn]: Invalid prop: type check failed for prop "message". Expected String, got Number.
(found in <Root>)
在撰写本文时,vue-test-utils
没有一个实用程序来测试这一点。我们可以使用jest.spyOn
来测试它:
it("message is of type string", () => {
let spy = jest.spyOn(console, "error");
cmp = createCmp({ message: 1 });
expect(spy).toBeCalledWith(
expect.stringContaining("[Vue warn]: Invalid prop")
);
spy.mockReset(); // or mockRestore() to completely remove the mock
});
在这里,我们监视console.error
函数,并检查它是否显示包含特定字符串的消息。这不是一个理想的检查方法,因为我们在监视全局对象并依赖于副作用。
幸运的是,有一种更简单的方法可以做到这一点,那就是选中vm.$options
。这里是 Vue 存储展开的组件选项的地方。通过扩展,我的意思是可以用不同的方式定义属性:
props: ["message"];
// or
props: {
message: String;
}
// or
props: {
message: {
type: String;
}
}
但它们最终都将以最扩展的对象形式结束(例如最后一个)。因此,如果我们检查第一个案例的cmp.vm.$option.props.message
,它们都将是{ type: X }
格式(尽管对于第一个示例,它将是{ type: null}
。
考虑到这一点,我们可以编写一个测试套件来测试message
属性是否具有预期的验证规则:
describe('Message.test.js', () => {
...
describe('Properties', () => {
...
describe('Validation', () => {
const message = createCmp().vm.$options.props.message
it('message is of type string', () => {
expect(message.type).toBe(String)
})
it('message is required', () => {
expect(message.required).toBeTruthy()
})
it('message has at least length 2', () => {
expect(message.validator && message.validator('a')).toBeFalsy()
expect(message.validator && message.validator('aa')).toBeTruthy()
})
})
定制活动
我们可以在自定义事件中至少测试两件事:
- 断言在操作之后触发事件
- 检查事件侦听器在被触发时是否调用
在MessageList.vue
和Message.vue
组件示例中,这将转换为以下内容:
- 断言
Message
组件在单击消息时触发message-clicked
。 - 检查
MessageList
以确保在触发message-clicked
时调用handleMessageClick
函数
首先,转到Message.vue
并使用$emit
触发该自定义事件:
<template>
<li
style="margin-top: 10px"
class="message"
@click="handleClick">
{{message}}
</li>
</template>
<script>
export default {
name: "Message",
props: ["message"],
methods: {
handleClick() {
this.$emit("message-clicked", this.message)
}
}
};
</script>
然后,在MessageList.vue
中,使用@message-clicked
处理事件:
<template>
<ul>
<Message
@message-clicked="handleMessageClick"
:message="message"
v-for="message in messages"
:key="message"/>
</ul>
</template>
<script>
import Message from "./Message";
export default {
name: "MessageList",
props: ["messages"],
methods: {
handleMessageClick(message) {
console.log(message)
}
},
components: {
Message
}
};
</script>
现在是编写单元测试的时候了。在test/Message.spec.js
文件中创建一个嵌套的describe
并准备断言Message
组件在点击我们前面提到的消息时触发message-clicked
的裸骨:
describe("Message.test.js", () => {
describe("Events", () => {
beforeEach(() => {
cmp = createCmp({ message: "Cat" });
});
it("calls handleClick when click on message", () => {
// @TODO
});
});
});
测试事件点击是否调用方法处理程序
我们可以测试的第一件事是,当点击一条消息时,handleClick
函数被调用。为此,我们可以使用包装器组件的trigger
和使用spyOn
函数的 Jest spy:
it("calls handleClick when click on message", () => {
const spy = spyOn(cmp.vm, "handleClick");
cmp.update(); // Forces to re-render, applying changes on template
const el = cmp.find(".message").trigger("click");
expect(cmp.vm.handleClick).toBeCalled();
});
参见cmp.update()
。当我们更改模板中使用的内容时-handleClick
,在本例中-,我们希望模板应用这些更改,我们需要使用update
函数。
请记住,通过使用间谍,将调用原始的handleClick
方法。你可能会故意想要这样;但是,通常,我们希望避免这种情况,只需检查单击时是否确实调用了该方法。为此,我们可以使用 Jest 模拟函数:
it("calls handleClick when click on message", () => {
cmp.vm.handleClick = jest.fn();
cmp.update();
const el = cmp.find(".message").trigger("click");
expect(cmp.vm.handleClick).toBeCalled();
});
在这里,我们完全替换了handleClick
方法,该方法可以在mount
函数返回的包装器组件的vm
上访问。
通过使用官方工具提供的setMethods
助手,我们可以更加轻松:
it("calls handleClick when click on message", () => {
const stub = jest.spy();
cmp.setMethods({ handleClick: stub });
cmp.update();
const el = cmp.find(".message").trigger("click");
expect(stub).toBeCalled();
});
使用setMethods
是建议的方法,因为这是官方工具为我们提供的一种抽象,以防 Vue 内部发生变化。
测试点击的自定义事件消息是否发出
我们已经测试了 click 方法调用它的处理程序,但是还没有测试处理程序本身是否发出message-clicked
事件。我们可以直接调用handleClick
方法,结合 Vuevm``$on
方法使用 Jest Mock 函数:
it("triggers a message-clicked event when a handleClick method is called", () => {
const stub = jest.fn();
cmp.vm.$on("message-clicked", stub);
cmp.vm.handleClick();
expect(stub).toBeCalledWith("Cat");
});
请看,这里我们使用的是toBeCalledWith
,因此我们可以准确地断言我们期望的参数,从而使测试更加健壮。这并不是说我们在这里没有使用cmp.update()
,因为我们没有进行任何需要传播到模板的更改。
测试@message clicked 是否触发事件
对于自定义事件,我们不能使用trigger
方法,因为它只用于 DOM 事件。但是,我们可以通过获取Message
组件并使用其vm.$emit
方法,自行发出事件。因此,在MessageList.test.js
中增加以下测试:
it("Calls handleMessageClick when @message-click happens", () => {
const stub = jest.fn();
cmp.setMethods({ handleMessageClick: stub });
cmp.update();
const el = cmp.find(Message).vm.$emit("message-clicked", "cat");
expect(stub).toBeCalledWith("cat");
});
我将让你来测试handleMessageClicked
的功能。
收工
在本章中,我们探讨了几个测试属性和事件的案例。官方的 Vue 测试工具vue-test-utils
确实让这变得容易多了。
版权属于:月萌API www.moonapi.com,转载请注明出处