简单案例学习Vue.js单元测试

发表于:2017-12-06 10:19

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:mvpzx    来源:51Testing软件测试网采编

  前言
  使用vue-cli可以直接生成一个包含unit & e2e测试的开发环境。这里我们主要针对unit文件进行单元测试
  (本发首发于我的笔记网站)
  命令行效果预览
  test/unit 文件结构及分析
  ├── coverage
  ├── jest.conf.js
  ├── setup.js
  └── specs
      ├── api-test.spec.js
      ├── click-test.spec.js
      ├── data-test.spec.js
      ├── dom-test.spec.js
      ├── input-test.spec.js
      ├── mock-test.spec.js
      └── props-test.spec.js
  ●coverage: 单元测试覆盖率的报表生成文件(当执行npm run unit后,可在该文件夹下打开index.html文件查看覆盖率)。
  ●jest.conf.js: jest的配置文件。Jest是Facebook开发的一个对javascript进行单元测试的工具,之前仅在其内部使用,后开源,并且是在Jasmine测试框架上演变开发而来,使用了我们熟知的expect(value).toBe(other)这种断言格式。
  ●setup.js: 默认生成的vue配置文件(不用更改,默认开发环境)。
  ●specs: 编写单元测试的文件夹(所有的测试文件都将放在该文件夹下)。
  data方法测试
  假设我们编写了这样一个组件:
  <!-- data-test.vue -->
  <template>
    <span>{{ msg }}</span>
  </template>
  <script>
  export default {
    name: 'TestData',
    data() {
      return {
        msg: 'hello'
      }
    },
    
    created() {
      this.msg = 'bye'
    }
  }
  </script>
  那么,我们可以这样编写断言
  // data.spec.js
  // 导入vue.js和组件,进行测试
  import Vue from 'vue'
  import TestDataComponent from '@/components/data-test.vue'
  describe('TestDataComponent', () => {
    // 检查原始组件选项
    it('has a created hook', () => {
      expect(typeof TestDataComponent.created).toEqual('function')
    })
    // 评估原始组件选项中的函数的结果
    it('should set the correct default data', () => {
      expect(typeof TestDataComponent.data).toEqual('function')
      const data = TestDataComponent.data()
      expect(data.msg).toEqual('hello')
    })
    // 检查mount中的组件实例
    it('should correctly set the msg when created', () => {
      const vm = new Vue(TestDataComponent).$mount()
      expect(vm.msg).toEqual('bye')
    })
    // 创建一个实例并检查渲染输出
    it('should render correct msg', () => {
      const Ctor = Vue.extend(TestDataComponent)
      const vm = new Ctor().$mount()
      expect(vm.$el.textContent).toEqual('bye')
    })
  })
  props测试
  很多组件的渲染输出由它的 props 决定。事实上,如果一个组件的渲染输出完全取决于它的 props,那么它会让测试变得简单,就好像断言不同参数的纯函数的返回值。例子:
  <template>
    <!-- props-test.vue  -->
    <p>{{ msg }}</p>
  </template>
  <script>
  export default {
    props: ['msg']
  }
  </script>
  那么我们就可以在不同的 props 中,通过 propsData 选项断言它的渲染输出:
  // props.spec.js
  import Vue from 'vue'
  import TestPropsComponent from '@/components/props-test.vue'
  const Ctor = Vue.extend(TestPropsComponent);
  // 针对props,可以通过propsData选项断言它的渲染输出
  const getRenderText = (propsData) => {
    const vm = new Ctor({propsData}).$mount()
    return vm.$el.textContent
  }
  describe('TestPropsComponent', () => {
    it('should render correctly with different props', () => {
      expect(getRenderText({msg: 'Hello'})).toEqual('Hello')
      expect(getRenderText({msg: 'Bye'})).toEqual('Bye')
    })
  })
  断言异步更新
  由于 Vue 进行 异步更新 DOM 的情况,一些依赖 DOM 更新结果的断言必须在 Vue.nextTick 回调中进行:
  <template>
    <!-- dom-test.vue -->
    <span>{{ msg }}</span>
  </template>
  <script>
  export default {
    data() {
      return {
        msg: 'Hello'
      }
    }
  }
  </script>
  断言的编写:
  // dom.spec.js
  import Vue from 'vue'
  import DomUpdateComponent from '@/components/dom-test.vue'
  describe('DomUpdateComponent', () => {
    it('the msg should be Hello', () => {
      const data = DomUpdateComponent.data()
      expect(data.msg).toEqual('Hello')
    })
    // 在状态更新后检查生成的HTML
    it('the msg should change to Bye', () =>  {
      const vm = new Vue(DomUpdateComponent).$mount()
      vm.msg = 'Bye'
      // 在状态改变后和断言 DOM 更新前等待一刻
      Vue.nextTick(() => {
        expect(vm.$el.textContent).toEqual('Bye')
      })
    })
  })
  组件事件的测试
  假设我们编写了这样一个组件,其中有一个按钮,按钮有一个点击事件:
  <!-- click-test.vue -->
  <template>
    <div>
      <p>现在是{{ num }}</p>
      <button @click="increase">change</button>
    </div>
  </template>
  <script>
  export default {
    data() {
      return {
        num: 0
      }
    },
    methods: {
      increase() {
        this.num++
      }
    }
  }
  </script>
  那么,我们可以这样在断言中触发事件:
  // click.spec.js
  import Vue from 'vue'
  import ClickTestComponent from '@/components/click-test.vue'
  describe('ClickTestComponent', () => {
    const Ctor = Vue.extend(ClickTestComponent)
    const vm = new Ctor().$mount()
    // 获取按钮
    const oBtn = vm.$el.querySelector('button')
    // 创建点击事件
    const clickEvent = new window.Event('click')
    it("the num should be 1", () => {
      // 触发按钮点击事件
      oBtn.dispatchEvent(clickEvent)
      // 需要手动监听更新
      vm._watcher.run()
      expect(vm.$el.textContent).toContain('1')
    })
    it("the num should be 2", () => {
      // 触发按钮点击事件
      oBtn.dispatchEvent(clickEvent)
      // 需要手动监听更新
      vm._watcher.run()
      
      expect(vm.$el.textContent).toContain('2')
    })
  })
  用户交互组件的测试
  假设我们有一个输入框,并且绑定了一些事件:
  <template>
    <!-- input-test.vue -->
    <div>
      <p>{{ msg }}</p>
      <input type="text" v-model="input">
      <button @click="handle"></button>
    </div>
  </template>
  <script>
  export default {
    data() {
      return {
        msg: '',
        input: 'hi'
      }
    },
    methods: {
      handle() {
        this.msg = this.input
      }
    }
  }
  </script>
  很显然,我们是要利用v-model模拟input进行断言:
  // input.spec.js
  import Vue from 'vue'
  import InputTestComponent from '@/components/input-test.vue'
  describe('InputTestComponent', () => {
    const Ctor = Vue.extend(InputTestComponent)
    const vm = new Ctor().$mount()
    const oBtn = vm.$el.querySelector('button')
    const clickEvent = new window.Event('click')
    it('the msg should be equal with input', () => {
      vm.input = 'let me change msg to what I say'
      oBtn.dispatchEvent(clickEvent)
      // 手动监听更新
      vm._watcher.run()
      expect(vm.$el.textContent).toContain('let me change msg to what I say')
    })
  });
  API的测试
  1.利用mock.js模拟后端接口
  在src文件夹下新建mock文件夹,编写index.js:
  // mock/index.js
  import Mock from 'mockjs'
  export default Mock.mock('http://api.rs.com/v1/test', {
    'greeting': 'Hi, Vue'
  });
  2.利用axios进行ajax
  在src文件夹下新建provider文件夹,编写index.js:
  // provider/index.js
  import axios from 'axios'
  import '../mock/index'
  const getGreeting = async (url = 'http://api.rs.com/v1/test') => {
    try {
      const res = await axios.get(url)
      return res.data
    } catch (err) {
      throw new Error('获取问候语失败')
    }
  }
  export default getGreeting
  3.编写组件
  <template>
    <!-- mock-test.vue -->
    <p>{{ msg }}</p>
  </template>
  <script>
  import getGreeting from '@/provider/index'
  export default {
    data() {
      return {
        msg: 'waiting'
      }
    },
    created() {
      this.getMsg()
    },
    methods: {
      async getMsg(url) {
        try {
          const data = await getGreeting(url)
          this.msg = data.greeting
        } catch (err) {
          console.log(err.message)
        }
      }
    }
  }
  </script>
  4.针对provider/index.js进行单元测试
  // api.spec.js
  import getGreeting from '@/provider/index'
  describe('test greeting', async () => {
    it('res should be "Hi, Vue"', async () => {
      const res = await getGreeting();
      expect(res.greeting).toEqual('Hi, Vue')
    })
    it('err should be catched', async () => {
      try {
        const res = await getGreeting('wrongUrl')
      } catch (err) {
        expect(err.message).toEqual('获取问候语失败')
      }
    })
  })
  5.针对编写的组件进行测试
  // mock-test.spec.js
  import Vue from 'vue'
  import MockTestComponet from '@/components/mock-test.vue'
  describe('MockTestComponet', () => {
    it('should have the created hook', () => {
      expect(typeof MockTestComponet.created).toEqual('function')
    })
    it('the msg should be waiting', () => {
      const data = MockTestComponet.data()
      expect(data.msg).toEqual('waiting')
    })
    it('the msg should change when created', async () => {
      // const vm = new Vue(MockTestComponet).$mount()
      const Ctor = Vue.extend(MockTestComponet)
      const vm = new Ctor().$mount()
      setTimeout(() => {
        expect(vm.msg).toEqual('Hi, Vue')
      }, 0)
    })
    // 返回正确的结果
    it('test methods getMsg by correct url', async () => {
      const vm = new Vue(MockTestComponet)
      await vm.getMsg();
      expect(vm.msg).toEqual('Hi, Vue')
    })
    // 抛出异常
    it('test methods getMsg by wrong url', async () => {
      try {
        const vm = new Vue(MockTestComponet)
        await vm.getMsg('fuck');
      } catch (err) {
        expect(err.message).toEqual('获取问候语失败')
      }
    })
  })
  结束语
  本单元测试的源码放在了我的github上(点击前往

   上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号