1、背景

日常业务开发中,任务多,项目时间紧,产品和项目催得急,另外技术领导也并未强制要求编写单元测试,所以国内很大一部分程序员是不会去编写单元测试代码。我们团队也是(捂脸)。
最近在抽出业务中用到的组件,并单独剥离出来形成一个UI组件库;在参考学习业界优秀团队的开源组件库时,看到绝大部分开源组件库的单元测试覆盖率是非常高。因此,也在尝试编写一个组件,同时编写单元测试代码。

2、单元测试的含义

单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。(源自百度百科)

3、Vue项目单元测试相关技术介绍

jest

jest是Vue官方放在最前面介绍的测试框架,由facebook出品。但是mocha也是一个非常优秀的测试框架。可以根据业务情况和团队来选择。

Vue Test Utils

Vue Test Utils 是 Vue.js 官方的单元测试实用工具库,提供更多详细的指引和自定义设置。

明白要测试的是什么

对于 UI 组件来说,我们不推荐一味追求行级覆盖率,因为它会导致我们过分关注组件的内部实现细节,从而导致琐碎的测试。
取而代之的是,我们推荐把测试撰写为断言你的组件的公共接口,并在一个黑盒内部处理它。一个简单的测试用例将会断言一些输入 (用户的交互或 prop 的改变) 提供给某组件之后是否导致预期结果 (渲染结果或触发自定义事件)。

4、撸起袖子就是干(编写单元测试)

示例代码,仅供参考

第一步: 安装初始化一个项目

选择使用vue-cli3创建了一个项目,选择jest作为测试框架.

  1. vue create vue-jest-test
  • 新建项目test目录下有一个hello world组件单元测试示例文件,一起看一下

    helloworld.spec.js

  1. import {shallowMount} from '@vue/test-utils';
  2. import HelloWorld from '@/components/HelloWorld.vue';
  3. describe('HelloWorld.vue', () => {
  4. it('renders props.msg when passed', () => {
  5. const msg = 'new message';
  6. const wrapper = shallowMount(HelloWorld, {
  7. propsData: {msg}
  8. });
  9. expect(wrapper.text()).toMatch(msg)
  10. });
  11. });

第二步:新建一个card组件并编写单元测试

  • components/card/index.vue```vue
  1. <template>
  2. <div class="card">
  3. <div class="card-header" v-if="$slots.header">
  4. <slot name="header"></slot>
  5. </div>
  6. <div class="card-body" v-if="$slots.body">
  7. <slot name="body"></slot>
  8. </div>
  9. </div>
  10. </template>
  11. <script>
  12. export default {
  13. name: 'card',
  14. props: {
  15. },
  16. created() {
  17. console.log(this.$slots)
  18. }
  19. }
  20. </script>
  21. <style lang="scss" scoped>
  22. .card {
  23. border-radius: 12px;
  24. background-color: #ffffff;
  25. margin-bottom: 15px;
  26. }
  27. .card-header {
  28. font-size: 19px;
  29. color: #000000;
  30. padding: 12px 15px;
  31. }
  32. .card-body {
  33. padding:0 15px;
  34. min-height: 60px;
  35. }
  36. </style>
  • 在test目录下新建card目录card.spec.js

card目录下包含模拟的header内容和body内容
根据组件的设计需要测试的内容:

  • 测试能否正常渲染card组件
  • 测试传入slot=header的时候是否正常
  • 测试传入slot=body的时候是否正常

card.spec.js具体代码

  1. import {mount} from '@vue/test-utils'
  2. import Card from '@/components/card/index.vue';
  3. import CardHeader from './header';
  4. import CardBody from './body';
  5. describe('card.vue', () => {
  6. it('renders empty card', () => {
  7. const wrapper = mount(Card);
  8. expect(wrapper.findAll('div')).toHaveLength(1);
  9. });
  10. it('renders pass a slot = header', () => {
  11. const wrapper = mount(Card, {
  12. slots: {
  13. header: CardHeader, // 将匹配 `<slot name="header" />`。
  14. }
  15. });
  16. expect(wrapper.find({name: 'header'}).is(CardHeader)).toBe(true);
  17. });
  18. it('renders pass a slot = body', () => {
  19. const wrapper = mount(Card, {
  20. slots: {
  21. header: CardBody, // 将匹配 `<slot name="header" />`。
  22. }
  23. });
  24. expect(wrapper.find({name: 'body'}).is(CardBody)).toBe(true);
  25. })
  26. });
  • 运行单元测试
  1. // 运行所有的单元测试文件
  2. npm run test:unit
  3. // 运行单个单元测试文件
  4. npm run test:unit card/card.spec.js

运行成功后如下图

unit.png

参考资料 Vue中的单元测试 vue-test-utils中文文档 jest文档