lsekfe 发表于 2023-6-13 10:46:52

浅谈vue单元测试最佳实践(中)

三、开始单元测试
  1、整合的官方demo
  //src\components\hCounter\counter.vue
  <template>
  <div>
      <span class="desc">{{ desc }}</span>
      <span class="count">{{ count }}</span>
      <button @click="increment">Increment</button>
  </div>
  </template>
  <script>
  export default {
      data() {
        return {
        count: 0
        }
      },
      props: {
        desc: {
        type: String,
        default: "Counter"
        }
      },
      methods: {
        increment() {
        this.count++
        }
      }
  }
  </script>


  2、编写单元测试用例
  //src\components\__test__\counter.spec.js
  import { mount } from '@vue/test-utils'
  import Counter from '@/components/hCounter/counter';
  describe('Counter', () => {
  // 现在挂载组件,你便得到了这个包裹器
  const wrapper = mount(Counter, {
      propsData: {
        desc: 'I am a counter'
      }
  })
  console.log('wrapper.vm.desc' , wrapper.vm.desc)
  console.log('wrapper.vm.count' , wrapper.vm.count)
  console.log('options.attachedToDocument' , wrapper.options.attachedToDocument)
  // console.log('wrapper.element' , wrapper.element)
  it('renders the desc', () => {
      expect(wrapper.html()).toContain('I am a counter')
  });
  it('renders the correct markup', () => {
      expect(wrapper.html()).toContain('<span class="count">0</span>')
  })
  // 也便于检查已存在的元素
  it('has a button', () => {
      expect(wrapper.find('button').exists()).toBe(true)
      // console.log(expect(wrapper.find('button')))
      // expect(wrapper.contains('button')).toBe(true)
  })
  //模拟用户交互
  //用户点击按钮则计数器增加递增,首先通过wrapper.find()定位改按钮,改方法返回一个该按钮元素的包裹器,然后对按钮包裹器调用.trigger模拟操作
  it('button click should increment the count', () => {
      expect(wrapper.vm.count).toBe(0)
      const button = wrapper.find('button')
      button.trigger('click')
      expect(wrapper.vm.count).toBe(1)
  })
  it('set count to 100', async () => {
      await wrapper.setData({'count': 100});
      expect(wrapper.vm.count).toBe(100)
  })
  //为了测试计数器中的文本是否更新,需要使用异步nextTick await
  //任何导致操作DOM的改变都应该在断言之前await
  it('button click should increment the count text', async () => {
      expect(wrapper.text()).toContain('1')
      const button = wrapper.find('button')
      await button.trigger('click')
      expect(wrapper.text()).toContain('2')
  })
  })


  3、对UI单元测试的建议
  对于 UI 组件来说,我们不推荐一味追求行级覆盖率,因为它会导致我们过分关注组件的内部实现细节,从而导致琐碎的测试。
  取而代之的是,我们推荐把测试撰写为断言你的组件的公共接口,并在一个黑盒内部处理它。一个简单的测试用例将会断言一些输入 (用户的交互或 prop 的改变) 提供给某组件之后是否导致预期结果 (渲染结果或触发自定义事件)。
  比如,对于每次点击按钮都会将计数加一的 Counter 组件来说,其测试用例将会模拟点击并断言渲染结果会加 1。该测试并没有关注 Counter 如何递增数值,而只关注其输入和输出。
  该提议的好处在于,即便该组件的内部实现已经随时间发生了改变,只要你的组件的公共接口始终保持一致,测试就可以通过。
  4、某项目首页tab的单元测试
  大致的逻辑上是,未登录的时候有4个tab分别是主页、产品介绍、案例展示、联系我们。
  登陆之前:
http://www.51testing.com/attachments/2023/06/15326880_2023060915045718RZ6.jpg
  登陆之后从home页返回之后会把主页去掉,新的tab是返回系统、产品介绍、业务指南、培训视频、案例展示、联系我们,且点击返回系统会根据角色跳转到不同的页面。
http://www.51testing.com/attachments/2023/06/15326880_202306091505001tSkK.jpg
  完整的单元测试用例如下~
  import { mount, createLocalVue } from '@vue/test-utils'
  import loginHeader from '@/components/loginHeader'
  import VueRouter from 'vue-router'
  const localVue = createLocalVue()
  localVue.use(VueRouter)
  const router = new VueRouter()
  describe('loginHeader',() => {
      const wrapperNotLogin = mount(loginHeader, {
        localVue,
        router
      });
      it('未登录之前的tab展示', () => {
        const loginTabWrap = wrapperNotLogin.find('.login-tab-wrap');
        const tabsArr = ['主页', '产品介绍', '案例展示', '联系我们'];
        for(let i = 1; i < 5; i++) {
              const findDom = loginTabWrap.findAll('div').at(i);
              expect(findDom.html()).toContain(tabsArr)
        }
      });
      //模拟登陆之后的情况
      sessionStorage.setItem('objUser', 'yes')
      sessionStorage.setItem('system.route.path.type', 'PLAN_MANAGER')
      const wrapperLogin = mount(loginHeader, {
        mocks: {
              $route: {//伪造路由
                  path: '/home'
              },
              $router: {}
        }
      });
      it('登陆成功且是计划管理人身份', () => {
        const loginTabWrap = wrapperLogin.find('.login-tab-wrap');
        const tabsArr = ['返回系统', '产品介绍', '业务指南', '培训视频', '案例展示', '联系我们'];
        for(let i = 1; i < 7; i++) {
              const findDom = loginTabWrap.findAll('div').at(i);
              expect(findDom.html()).toContain(tabsArr)
        }
      });
      it('when system.route.path.typ == PLAN_MANAGER tabsArr.toRouter == /absProductManage/index?active=1', () => {
        expect(wrapperLogin.vm.tabArr.toRouter).toBe('/absProductManage/index?active=1')
      });

  })


  npm run test之后的结果如下,通过了单元测试。
http://www.51testing.com/attachments/2023/06/15326880_202306091505041xNR3.jpg

页: [1]
查看完整版本: 浅谈vue单元测试最佳实践(中)