• 如何为我的VUE项目编写高效的单元测试--Jest


    Unit Testing(单元测试)--Jest

    一个完整的测试程序通常由几种不同的测试组合而成,比如end to end(E2E)测试,有时还包括整体测试、简要测试和单元测试。这里要介绍的是Vue中的单元测试,使用流行的Jest JavaScript测试库来运行我们的测试。

    1.测试的目的

    • 排除故障

      每个应用的开发中,多少会出现一些意料之外的bug。通过测试应用程序,可以帮助我们大大减少此类问题,并增强应用程序的逻辑性。

    • 保证团队成员的逻辑统一

      如果您是团队的新成员,并且对应用程序还不熟悉,那么一组测试就好像是有经验的开发人员监视你编写代码,确保您处于代码应该执行的正确路线之内。通过这些测试,您可以确信在添加新功能或更改现有代码时不会破坏任何东西。

    • 可以提高质量代码

      当您在编写vue组件时,由于考虑到测试,最好的方案将是创建独立的、更可重用的组件。如果您开始为您的组件编写测试,并且您注意到这些组件不容易测试,那么您可能会重构您的组件,最终起到改进它们的效果。

    • 起到很好的说明文档作用

      正如在第一点中提到的,测试的另一个结果是,它最终可以为您的开发团队生成良好的文档。当某人对代码库还不熟悉时,他们可以查看测试以获得指导,这可以提供关于组件应该如何工作的意图的洞察,并为可能要测试的边缘部分提供线索。

    1.我们需要测试什么

    在测试的过程中,很容易照成过分的测试一些不需要的东西,这会不必要地减慢开发时间。那么我们在Vue.js应用程序中测试什么呢?答案其实很简单:组件。由于Vue应用程序是一个个组件相互组合而成的,我们需要测试它们各自的行为,以确保它们正常工作。

    组件原理及分解

    我们可以先分解组件,看看组件之间是怎么协调工作的。

    一般来说,组件需要遵照我们定义的逻辑来工作,这里有一个输入和输出的概念。组件会接收一些静态或动态的输入值,然后依照逻辑输出一些值或者是dom元素。

    正常来说,组件的输入输出有以下这些:

     1// 输入值
    2
    3// 静态接收数据
    4Component Data
    5// 动态接收数据
    6Component Props
    7// 用户互交,例如:一个用户单击的按钮
    8User Interaction 
    9// 生命周期逻辑,例如:mounted(), created()
    10Lifecycle Methods
    11// 组件状态值
    12Vuex Store
    13// 路由参数
    14Route Params
    15
    16// 输出值
    17
    18// 输出的dom
    19What is rendered to the DOM
    20// 外部调用的函数
    21External function calls
    22// 组件触发的事件
    23Events emitted by the component
    24// 路由的变更
    25Route Changes
    26// 组件状态值得更新
    27Updates to the Vuex Store
    28// 对子组件的改变
    29Connection with children
    30

    我们可以识别组件的输入和输出,挑选我们应该测试的内容,从而避开测试组件的内部业务逻辑。换句话说,我们不应该因为担心每一行代码如何工作而陷入困境。

    这看起来可能有悖常理,但是单元测试的目标纯粹是确保组件产生预期的结果。我们在这里不关心它是如何得出这个结果的。后期我们甚至可能会改变我们的逻辑方式,所以我们不希望测试如何实现这些逻辑。这些逻辑不是测试的工作。就单元测试而言,我们只要确保组件的输出正常就行了。

    要测试的部分

     1<template>
    2    <div>
    3        <button v-show="loggedIn">Logout</button>
    4    </div>
    5</template>
    6
    7<script>
    8export default {
    9    data() {
    10        return {
    11            loggedInfalse
    12        }
    13    }
    14}
    15
    </script>

    在这个例子中,我们有一个组件,如果loggedIn属性为true,它将显示一个logout按钮。为了弄清楚我们应该测试这个组件的哪个部分,我们的第一步是确定组件的输入和输出。

     1input
    2
    3// 静态数据
    4data => loggedIn
    5// 这个数据属性决定按钮是否显示,所以这是一个我们应该测试的输入
    6
    7output
    8
    9Dom输出(button)
    10根据输入(loggedIn),我们的按钮应该在什么时候显示在DOM中

    对于更复杂的组件,将有更多的方面需要测试,但同样的方法也适用。

    不测试的部分

    对你应该测试的东西知道大概当然是必要的,知道你不应该测试的东西也是有帮助的。如下:

    • 不测试组件逻辑的详情(implementation details)

      当单元测试时,我们不需要为某些逻辑是如何工作而烦恼,只要它们确实工作就行了。我们不在乎这里的内部结构。我们只关心组件产生了我们期望的输出。

    • 不要测试框架本身

      开发人员经常试图测试太多,包括框架本身的内部工作。但是框架作者已经建立了这样的测试。

      例如,props中的数据类型,如果我们尝试传入错误数据,Vue将抛出错误。我们不需要浪费时间测试Vue.js框架。这包括不要对Vue路由器和Vuex进行不必要的测试。

    • 不测试第三方库

      如果您使用的第三方库是高质量的,那么它们已经有了自己的测试。我们不需要测试它们的内部结构。例如,我们不需要测试Axios是如何工作的。Axios队为我们做了这件事。

    2.编写单元测试

    当我们使用Vue CLI创建应用项目时:

    1. 首先需要选择Manually select features自定义一些项目配置
    2. 然后在选择项目依赖时,勾选Unit Testing选项
    3. 最后在选择测试方案时。选择Jest
    • vue/cli项目可以这样安装
    1vue add @vue/unit-jest
    2
    3✔  Successfully installed plugin: @vue/cli-plugin-unit-jest
    4// 如果安装的jest运行报错可能是版本问题,可以尝试npm update

    这样创建的项目中就为我们安装好了依赖。

    打开项目后,让我们从查看package.json开始,在这里我们将看到为我们安装了Jest和vue测试实用程序。

    1// package.json
    2"devDependencies": {
    3    "@vue/cli-plugin-unit-jest""^3.11.0",
    4    "@vue/test-utils""1.0.0-beta.29"
    5}

    Jest是一个JavaScript测试框架,它专注于简化单元测试。Jest将为我们运行单元测试,并在测试通过或失败时向我们报告。

    API list:

     1afterAll(fn, timeout)
    2afterEach(fn, timeout)
    3beforeAll(fn, timeout)
    4beforeEach(fn, timeout)
    5describe(name, fn)
    6describe.each(table)(name, fn, timeout)
    7describe.only(name, fn)
    8describe.only.each(table)(name, fn)
    9describe.skip(name, fn)
    10describe.skip.each(table)(name, fn)
    11test(name, fn, timeout)
    12test.each(table)(name, fn, timeout)
    13test.only(name, fn, timeout)
    14test.only.each(table)(name, fn)
    15test.skip(name, fn)
    16test.skip.each(table)(name, fn)
    17test.todo(name)

    @vue/test-utils是Vue.js的官方单元测试实用程序库。它使我们能够在测试中渲染组件,然后对这些渲染的组件执行各种操作。这使得我们可以测试组件的运行结果。

    如何运行Jest测试,我们来查看package.json中的运行脚本

    1"scripts": {
    2    ...
    3    "test:unit""vue-cli-service test:unit"
    4}
    5// 在我们编写好测试代码后只要运行:
    6npm run test:unit
    7// 或者在CLI中运行测试就可以运行测试代码
    8// 这个命令的意思就是查看tests/unit目录,并运行目录下名为[componentsName].spec.js的文件。

    查看项目根目录下的tests/unit文件夹,有一个初始化的Example.spec.js文件。spec是specification的缩写,即详述组件运行规则的js文件

    • ## demo1(测试一个方法)

    创建函数文件demo1.js

    1function sum(a, b{
    2  return a + b;
    3}
    4module.exports = sum;

    在tests/unit/中创建demo1.spec.js

     1// 导入要测试的文件
    2const sum = require('./sum')
    3
    4// 使用test(name, fn, timeout)创建一个测试
    5test('adds 1 + 2 to equal 3', () => {
    6  expect(sum(12)).toBe(3)
    7})
    8
    9// 其中,name是对测试的描述,测试结果会打印
    10// fn是要执行的测试
    11// expect()方法中描述的是测试的结果
    12// toBe()是测试预期的值

    运行这个单元测试

    1npm run test:unit
    2
    3PASS  tests/unit/demo1.spec.js
    4✓ adds 1 + 2 to equal 3 (5ms)
    5// PASS表示测试通过,函数运行正常
    6// 下面列出每一项text(),[test.name (测试使用的时间)]
    • ## demo2(测试一个组件)

    创建组件demo2.vue

     1<template>
    2    <div>
    3        <button v-show="loggedIn">Logout</button>
    4    </div>
    5</template>
    6<script>
    7export default {
    8    data() {
    9        return {
    10            loggedInfalse
    11        }
    12    }
    13}
    14
    </script>

    关于这个组件,前面我们已经介绍过了,要测试的点如下:

    1. 如果用户未登录,则不显示“注销”按钮
    2. 如果用户已登录,则显示“注销”按钮

    在tests/unit/中创建demo2.spec.js

     1// 导入要测试的组件
    2import demo2 from '@/components/demo2'
    3
    4// 因为我们要测试的是组件,所以我们在测试时需要加载这个组件
    5// `@vue/test-utils`为我们提供了这项功能
    6import { mount } from '@vue/test-utils'
    7// 使用describe(name, fn)创建一个测试组
    8// 当我们有多个测试时,用这种方式组织它们比较具有逻辑性
    9
    10describe('ComponentsDemo2', () => {
    11
    12    // 其中写所有组件内需要的测试
    13    test('if user is not logged in, do not show logout button', () => {
    14        // 安装组件
    15        const wrapper = mount(AppHeader)
    16
    17        // 默认登录状态是false,则找到组件内的btn,查看其可见性为false
    18        expect(wrapper.find('button').isVisible()).toBe(false)
    19    })
    20    test('if a user is logged in, show logout button', () => {
    21        // 安装组件
    22        const wrapper = mount(AppHeader)
    23
    24        // 要测试登录状态是‘已登录’,则先设置其状态值
    25        wrapper.setData({ loggedIn: true })
    26
    27        // 这时找到组件内的btn,查看其可见性为true
    28        expect(wrapper.find('button').isVisible()).toBe(true)
    29    })
    30})

    提示:

    您可能还会看到使用it()的测试块,它是test()的别名。

    @vue/test-utils,还提供shallowMount()方法。如果组件有子组件,shallowMount()将返回该组件的本身,而不是完全渲染的详细组件单元测试的焦点是单一的组件,多数时候我们会忽略其子组件。

    运行这个单元测试

     1npm run test:unit
    2
    3PASS  tests/unit/demo2.spec.js
    4ComponentsDemo2
    5    ✓ if user is not logged in, do not show logout button (20ms)
    6    ✓ if a user is logged inshow logout button (30ms)
    7
    8Test Suites: 1 passed, 1 total
    9Tests:       2 passed, 2 total
    10Snapshots:   0 total
    11Time:        2.653s
    12Ran all test suites.

    步骤总结如下图:

    1. 创建一个测试组:describe()
    2. 开始一个测试:test()
    3. 安装渲染组件:mount()
    4. 必要的时候设置组件参数:setData()
    5. 调试预期的组件行为及数据正确与否:expect()

    长按二维码关注公众号

  • 相关阅读:
    最终版需求分析
    第九次会议
    第八次会议
    软工测试文档
    软工第七次会议记录
    第六次会议
    软件工程第二次和第三次会议
    Java环境搭建与概述
    [JavaWeb] Ajax&JQuery
    [JavaWeb] JSP
  • 原文地址:https://www.cnblogs.com/qinyuandong/p/13649810.html
Copyright © 2020-2023  润新知