包装对象
在本节课中,我们将学习 `vue-test-utils` 的核心对象——Wrapper 对象。
到目前为止,我们已经使用 Jest 快照进行测试。在大多数情况下,这正是我们所需要的,但有时我们需要对某些具体的内容进行断言。
虽然可以通过 cmp.vm 访问 Vue 实例,但 vue-test-utils 提供了一系列实用工具,使操作更加便捷。让我们看看可以做些什么。
Wrapper 对象
Wrapper 是 vue-test-utils 的核心对象。它是由 mount、shallowMount、find 和 findAll 函数返回的类型。
find 和 findAll
它们接受一个 selector 作为参数,该参数既可以是 CSS 选择器,也可以是 Vue 组件。
因此我们可以执行如下操作:
1let cmp = mount(MessageList); 2expect(cmp.find(".message").element).toBeInstanceOf(HTMLElement); 3 4// 或者多次调用 5let el = cmp.find(".message").find("span").element; 6 7// 虽然上面这行代码也可以写成一次调用 8let el = cmp.find(".message span").element;
断言结构
让我们向 MessageList.test.js 添加更多测试:
MessageList.test.js
1import { mount } from '@vue/test-utils' 2import MessageList from '../src/components/MessageList' 3import Message from '../src/components/Message' 4 5describe('MessageList.test.js', () => { 6 let cmp 7 8 beforeEach(() => { 9 cmp = mount(MessageList, { 10 // 注意:props 会通过 propsData 覆盖 11 propsData: { 12 messages: ['Cat'] 13 } 14 }) 15 }) 16 17 it('接收到 ["Cat"] 作为 messages 属性', () => { 18 expect(cmp.props().messages).toEqual(['Cat']) 19 }) 20 21 it('具有预期的 HTML 结构', () => { 22 expect(cmp.element).toMatchSnapshot() 23 }) 24 25 it("是一个 MessageList 组件", () => { 26 expect(cmp.is(MessageList)).toBe(true); 27 28 // 或使用 CSS 选择器 29 expect(cmp.is("ul")).toBe(true); 30 }); 31 32 it("包含一个 Message 组件", () => { 33 expect(cmp.contains(Message)).toBe(true); 34 35 // 或使用 CSS 选择器 36 expect(cmp.contains(".message")).toBe(true); 37 });
在这里,我们使用 is 来断言根组件类型,使用 contains 检查子组件是否存在。与 find 类似,它们都接收一个选择器,可以是 CSS 选择器或组件。
我们还有一些工具可用于断言 Vue 实例:
MessageList.test.js
1import { mount } from '@vue/test-utils' 2import MessageList from '../src/components/MessageList' 3import Message from '../src/components/Message' 4 5describe('MessageList.test.js', () => { 6 let cmp 7 8 beforeEach(() => { 9 cmp = mount(MessageList, { 10 // 注意:props 会通过 propsData 覆盖 11 propsData: { 12 messages: ['Cat'] 13 } 14 }) 15 }) 16 17 it('接收到 ["Cat"] 作为 messages 属性', () => { 18 expect(cmp.props().messages).toEqual(['Cat']) 19 }) 20 21 it('具有预期的 HTML 结构', () => { 22 expect(cmp.element).toMatchSnapshot() 23 }) 24 25 it("是一个 MessageList 组件", () => { 26 expect(cmp.is(MessageList)).toBe(true); 27 28 // 或使用 CSS 选择器 29 expect(cmp.is("ul")).toBe(true); 30 }); 31 32 it("包含一个 Message 组件", () => { 33 expect(cmp.contains(Message)).toBe(true); 34 35 // 或使用 CSS 选择器 36 expect(cmp.contains(".message")).toBe(true); 37 }); 38 39 it("MessageList 和 Message 都是 Vue 实例", () => { 40 expect(cmp.isVueInstance()).toBe(true); 41 expect(cmp.find(Message).isVueInstance()).toBe(true); 42 });
现在我们将更详细地断言 结构:
MessageList.test.js
1import { mount } from '@vue/test-utils' 2import MessageList from '../src/components/MessageList' 3import Message from '../src/components/Message' 4 5describe('MessageList.test.js', () => { 6 let cmp 7 8 beforeEach(() => { 9 cmp = mount(MessageList, { 10 // 注意:props 会通过 propsData 覆盖 11 propsData: { 12 messages: ['Cat'] 13 } 14 }) 15 }) 16 17 it('接收到 ["Cat"] 作为 messages 属性', () => { 18 expect(cmp.props().messages).toEqual(['Cat']) 19 }) 20 21 it('具有预期的 HTML 结构', () => { 22 expect(cmp.element).toMatchSnapshot() 23 }) 24 25 it("是一个 MessageList 组件", () => { 26 expect(cmp.is(MessageList)).toBe(true); 27 28 // 或使用 CSS 选择器 29 expect(cmp.is("ul")).toBe(true); 30 }); 31 32 it("包含一个 Message 组件", () => { 33 expect(cmp.contains(Message)).toBe(true); 34 35 // 或使用 CSS 选择器 36 expect(cmp.contains(".message")).toBe(true); 37 }); 38 39 it("MessageList 和 Message 都是 Vue 实例", () => { 40 expect(cmp.isVueInstance()).toBe(true); 41 expect(cmp.find(Message).isVueInstance()).toBe(true); 42 }); 43 44 it("Message 元素存在", () => { 45 expect(cmp.find(".message").exists()).toBe(true); 46 }); 47 48 it("Message 不为空", () => { 49 expect(cmp.find(Message).isEmpty()).toBe(false); 50 }); 51 52 it('Message 的 class 属性值为 "message"', () => { 53 expect(cmp.find(Message).attributes().class).toBe("message"); 54 });
exists、isEmpty 和 attributes 方法在此类断言中非常有用。
断言样式
接下来,我们使用 classes 和 attributes().style 来断言 样式。由于 attributes().style 仅断言内联样式,我们先更新 Message.vue 组件添加样式:
Message.vue
1<li style="margin-top: 10px" class="message">{{message}}</li>
现在添加测试:
MessageList.test.js
1import { mount } from '@vue/test-utils' 2import MessageList from '../src/components/MessageList' 3import Message from '../src/components/Message' 4 5describe('MessageList.test.js', () => { 6 let cmp 7 8 beforeEach(() => { 9 cmp = mount(MessageList, { 10 // 注意:props 会通过 propsData 覆盖 11 propsData: { 12 messages: ['Cat'] 13 } 14 }) 15 }) 16 17 it('接收到 ["Cat"] 作为 messages 属性', () => { 18 expect(cmp.props().messages).toEqual(['Cat']) 19 }) 20 21 it('具有预期的 HTML 结构', () => { 22 expect(cmp.element).toMatchSnapshot() 23 }) 24 25 it("是一个 MessageList 组件", () => { 26 expect(cmp.is(MessageList)).toBe(true); 27 28 // 或使用 CSS 选择器 29 expect(cmp.is("ul")).toBe(true); 30 }); 31 32 it("包含一个 Message 组件", () => { 33 expect(cmp.contains(Message)).toBe(true); 34 35 // 或使用 CSS 选择器 36 expect(cmp.contains(".message")).toBe(true); 37 }); 38 39 it("MessageList 和 Message 都是 Vue 实例", () => { 40 expect(cmp.isVueInstance()).toBe(true); 41 expect(cmp.find(Message).isVueInstance()).toBe(true); 42 }); 43 44 it("Message 元素存在", () => { 45 expect(cmp.find(".message").exists()).toBe(true); 46 }); 47 48 it("Message 不为空", () => { 49 expect(cmp.find(Message).isEmpty()).toBe(false); 50 }); 51 52 it('Message 的 class 属性值为 "message"', () => { 53 expect(cmp.find(Message).attributes().class).toBe("message"); 54 }); 55 56 // 样式相关测试 57 it("Message 组件具有 .message 类", () => { 58 expect(cmp.find(Message).classes()).toContain("message"); 59 }); 60 61 it("Message 组件具有 style padding-top: 10px", () => { 62 expect(cmp.find(Message).attributes().style).toBe("margin-top: 10px;"); 63 });
在下一节课中,我们将测试目前完成的项目运行情况。