[Day18] Vue 3 单元测试 (Unit Testing) - Conditional rendering & Elements visibility

Conditional Rendering

在写元件时最常见的就是会使用 v-if 来动态插入和删除元素,我们马上来看看下面的范例程序。

const Component = {
  template: `
    <nav>
      <a id="profile" href="/profile">My Profile</a>
      <a v-if="admin" id="admin" href="/admin">Admin</a>
    </nav>
  `,
  data () {
    return {
      admin: false
    }
  }
}

在元件中有两个连结,一个为指向 /profile 的连结,另一个为指向 /admin 的连结,不过 /admin 连结只有在 admin 值为 true 时才会显示。

要测试这个元件是否正常,我们应该验证三种情况是否正确运行:

  • Case 1: 应该显示 /profile 连结。
  • Case 2: 当 admin 值为 false 时,不应该显示 /admin 连结。
  • Case 3: 当 admin 值为 true 时,应该显示 /admin 连结。

Case 1: 应该显示 /profile 连结

test('always render profile link', () => {
  const wrapper = mount(Component)
  const profileLink = wrapper.get('#profile')
  expect(profileLink.text()).toBe('My Profile')
})

语法说明:

  • get() : VueWrapper 有一个 get 方法来搜索现有元素,它使用和 Document.querySelector() 一样的语法,如果 get() 没有找到目标元素,它会抛出错误并导致测试失败。如果找到的话则会回传一个 DOMWrapper。

    DOMWrapper 是围绕 Wrapper API 的 DOM 元素的瘦包装器 (thin wrapper)。

  • text() : 回传元素的文本内容 (text content)。

Case 2: 当 admin 值为 false 时,不应该显示 /admin 连结。

test('does not render an admin link', () => {
  const wrapper = mount(Component)

  expect(wrapper.find('#admin').exists()).toBe(false)
})

语法说明:

  • find(): find() 和 get() 很像,一样是使用 Document.querySelector() 的语法,不过差别在於 find() 没有找到目标元素时不会抛出错误。

    除非断言的内容可能不存在,否则尽量都使用 get 而不是 find ,因为如果不存在时就表示真的有错误。

  • exist(): 检查元素是否存在。

Case 3: 当 admin 值为 true 时,应该显示 /admin 连结。

test('only render admin link when admin is true', () => {
  const wrapper = mount(Component, {
    data () {
      return {
        admin: true
      }
    }
  })

  expect(wrapper.get('#admin').text()).toBe('Admin')
})

语法说明:

  • mount() 的第二个参数是可以用来定义元件的状态 (state) 配置,例如 props, data, attrs 等等,因此这次我们就传入 data 覆盖掉元件中的预设值。

Checking Elements visibility

有时候我们只想隐藏一个元素,但不想将它从 DOM 中移除,此时我们可以使用 v-show 来实现这件事。

const Component = {
  template: `
    <nav>
      <a id="user" href="/profile">My Profile</a>
      <ul v-show="expandDropdown" id="user-dropdown">
        <!-- dropdown content -->
      </ul>
    </nav>
  `,
  data () {
    return {
      expandDropdown: false
    }
  }
}

不过和上面的例子不同的是,元素虽然不会渲染出来,但它其实是存在於 DOM tree 中的,所以如果使用 get() 或 find() 都会确实找到目标元素,但这其实不是我们想要的结果。

因此面对这样的情况,我们可以使用 isVisible() 来解决这个问题,isVisible 是专门用来检查元素是否为隐藏的状态,例如:

  • 元素或元素的祖先中有 display: none、visibility: hidden、opacity:0 的样式。
  • 元素或元素的祖先在收合的 <details> 标签中。
  • 元素或元素的祖先具有 hidden 属性。
test('does not show the user dropdown', () => {
  const wrapper = mount(Component)

  expect(wrapper.get('#user-dropdown').isVisible()).toBe(false)
})

善用 dataset

在写测试时,我们总是会透过 get() 或 find() 来寻找目标的元素,而通常我们要寻找元素时使用的选择器 (selectors) 可能习惯会是透过 id 或者是 class,然而我想建议大家使用 dataset 来当作选择器,如下:

import { mount } from '@vue/test-utils'

const Component = {
  template: '<div data-test="target">dataset</div>'
}

test('render dataset', () => {
  const wrapper = mount(Component)

  expect(wrapper.get('[data-test="target"]').text()).toBe('dataset')
})

原因是 id 或 class 通常都是因为其他用途而存在的,所以我们可能会在修改程序码的过程中而不小心去修改 id 或是 class 造成测试不通过,那如果是用 dataset 的话, 就不会有这个问题了,甚至在开发时也很清楚知道这段程序码和测试有关而特别小心修改。

参考资料


今天的分享就到这边,如果大家对我分享的内容有兴趣欢迎点击追踪 & 订阅系列文章,如果对内容有任何疑问,或是文章内容有错误,都非常欢迎留言讨论或指教的!

明天要来分享的是 Vue3 单元测试 (Unit Testing) 主题的第四篇 Event Handling ,那我们明天见!


<<:  Day19-React Router 篇-下篇

>>:  Unity与Photon的新手相遇旅途 | Day18-技能冷却

第12章:SSH远端连线设定与原理介绍(一)

前言 本章节,要讲的是SSH远端连线的机制与原理,以及SSH的使用方式。 什麽是OpenSSH? O...

Day 22 Ruby include vs extend vs prepend

include vs extend vs prepend include、extend、prepen...

Day 25 - Watch os 开发学习2(Button)

今天我们继续学习watch os的开发。 正文 上面所展示的是按下Button之後会将下面的Text...

Day20 JWT Token

What is JWT? JWT全名为JSON Web Token,是一种跨域认证的Solution...

[Day10] Vite 出小蜜蜂~Function Composition!

Day10 接下来,要帮 Squid 也装上 Laser, 敌人的 Laser 跟我们的外观是不一样...