前端自动化测试jest

持续集成和前端自动化测试

​ 持续集成是互联网软件开发上线流程中的核心一环,自动化测试是持续集成得以实现的核心步骤,缺乏了自动化测试,持续集成自然无从谈起。

​ 在日常的开发中,前端错综复杂的变化引发的 bug 往往令开发者头疼,或多或少经历过 修完东墙西墙倒 的经历,此时前端自动化测试就显得非常重要。

​ 前端的自动化测试无非也是编写测试用例,在持续集成时执行跑通全部测试用例。如果是一个短平快的小项目,引入前端自动化测试,编写测试用例,无疑只会增加开发成本,然而当项目扩大、迭代频繁、逻辑复杂、需求反复变更的情况下,回归测试的成本是巨额的,自动化测试的优势就能体现出来。

自动化测试的收益 = 迭代次数 * 全手动执行成本 - 首次自动化成本 - 维护次数 * 维护成本

​ 尽早引入前端自动化测试不仅能够减少项目 bug 出现概率 (尤其是回归测试中的 bug),还能更好地进行代码组织,增强项目的可维护性,尤其对于工程质量较差的项目,收益是巨大的;如果将其应用于持续集成中,commit 触发自动执行测试脚本,还能大幅提升团队的开发效率。

前端自动化分类

​ 单元测试
​ 单元测试,见名知意,可以理解为对系统的某个单元进行测试,而这个单元,可以是某个函数,某个组件,对于这种测试形式来说,我们只关注这个独立的单元的功能是否正常。测试用例以当前单元内的功能作为对象。

​ 集成测试
​ 将多个单元集成到一起,进行测试,重点关注各个单元串联起来之后的系统整体功能是否正常。此时的测试用例以多个单元组成的某个独立的系统为对象。

​ 前端自动化测试可以按照开发模式分两类:TDD (Test-Driven Development) 测试驱动开发、BDD (Behavior Driven Development) 行为驱动开发。

​ 测试还可以按照用例粒度分为 单元测试 (Unit Test)、集成测试 (Integration Test)、端到端测试 (End to End Test)。

TDD (Test-Driven Development) 测试驱动开发

​ TDD 顾名思义,开发者根据需求先编写测试用例,再逐步开发,最终满足全部测试用例的需求。

​ TDD 的基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需求分析,设计,质量控制量化的过程。

​ TDD 的重要目的不仅仅是测试软件,测试工作保证代码质量仅仅是其中一部分,而且是在开发过程中帮助客户和程序员去除模棱两可的需求。TDD 首先考虑使用需求(对象、功能、过程、接口等),主要是编写测试用例框架对功能的过程和接口进行设计,而测试框架可以持续进行验证。

​ 刚开始的时候,只有测试用例,未进行功能开发,执行测试用例,满屏是红色的测试用例不通过提示,随着测试用例被满足变绿,最终全部变绿,功能开发完成,因此前端自动化测试也被叫做 Red-Green Development。

TDD 先写测试再写代码,单位是模块,多用于 单元测试
重点在测试代码,属于 白盒测试
测试内容是模块,速度快,但是忽略模块间依赖,安全感低

​ 流程

  1. 编写测试用例
  2. 运行测试,测试用例无法通过测试
  3. 编写代码,时测试用例通过测试
  4. 优化代码,完成开发
  5. 重复上述步骤

​ 优势

  1. 长期减少回归 bug
  2. 代码质量更好(组织,可维护性)
  3. 测试覆盖率高
  4. 错误测试代码不容易出现

BDD (Behavior Driven Development) 行为驱动开发

​ 测试用例模拟用户的操作行为,通常在完成业务代码开发之后,以用户的操作为指导编写测试代码。当测试用例跑通之后,就可以认为系统的整体流程已经流畅。

​ BDD 的模式适用于平时的业务代码开发,因为业务的需求有可能变更频繁,但操作流程有可能不会变化,当业务代码发生变化的时候,可以使用原来的测试用例继续跑代码,节省了开发时间。

BDD 先写代码再写测试,测试单位是功能,多用于集成测试
重点在测试 UI(DOM)功能,属于黑盒测试
测试内容是整套操作流程,速度慢,往往需要多个模块配合,安全感高

TDD 开发模式更适用于开发,类似方法函数库,对于数据的处理,对于这种显示组件,更加推荐于BDD 的开发方式,这样既有了测试,也不会增加过多的工作负担,

在平时的项目中,通常使用 TDD 和 BDD 相结合来进行测试,TDD 负责方法类、独立组件的测试。BDD 则负责整体业务模块的测试。

前端自动化工具选择

前端近几年涌现出很多优秀的测试工具:

karma – Google Angular 团队开发的测试运行平台,配置简单灵活,能够很方便在多个真实浏览器中运行测试

mocha – 很优秀的测试框架,有完善的生态系统,简单的测试组织方式,不对断言库和工具做任何限制,非常灵活

jest – facebook 出品的大而全的测试框架,React 官方推荐的单元测试框架,配置简单运行速度快

还有很多其他的前端测试框架,但大同小异,无非是对断言和测试桩等工具的集成度不同,论成熟度首推 mocha,论效率首推 jest。

jest 是 Facebook 开源的 JavaScript 测试框架,它自动集成了断言、JsDom、覆盖率报告等开发者所需要的所有测试工具,是一款几乎零配置的测试框架,而且速度很快,此处选择 jest 作为测试工具。

- Jest测试框架优点

​ 比较新:喜新厌旧是人的天性,作为一个程序员,你更要有拥抱全新知识的态度。绝不能固步自封,顽固不化。

​ 基础很好:框架基础好就是性能好、功能多、简单易用,Jest在这三个方面你可以完全放心。

​ 速度快: 单独模块测试功能,比如说有两个模块A和B,以前都测试过了,这时候你只改动A模块,再次测试,模块B不会再跑一次,而是直接测试A模块。

API简单 :等你基础知识学完后,你就会发现API非常简单,数量也少。

​ 隔离性好:Jest里会有很多的测试文件等待我们使用,Jest的执行环境都是隔离,这样就避免不同的测试文件执行的时候互相影响而造成出错。

IDE整合:Jest直接可以和很多编辑器(VSCode)进行融合,让测试变的更加简单。

​ 多项目并行:比如我们写了Node.js的后台项目,用React写了一个前台项目,Jest是支持他们并行运行,让我们的效率更加提高了。

​ 快出覆盖率:(测试代码覆盖率) 对于一个项目的测试都要出覆盖率的,Jest就可以快速出这样的覆盖率统计结果,非常好用。

端到端测试(E2E)

区别

在 jest 单元测试中使用快照、API-mock 和 DOM 样式状态断言已经能够实现基础的 UI 测试,但是单元测试属于白盒测试,更关注数据的流动,而端到端测试 (End To End Test) 属于黑盒测试,更关注操作结果的展示,因此测试效果自然不同。端到端测试更贴近真实用户操作,页面运行在真实的浏览器环境中,因此端到端测试是从用户角度出发的测试。

工具选择

端到端测试的工具也有不少,最为突出的是老牌 e2e 测试工具 NightWatch,根据需要安装 Selenium 或其他 Webdriver,优势是可以测试多类浏览器,兼容性好,而 Cypress 是为现代网络打造的下一代前端测试工具,安装更简单,可以测试任何在浏览器中运行的内容,测试执行效率更高,此处选用 Cypress 作为端到端测试工具。

就像官网所说,Cypress 就像一个完整的烘烤箱,他还自带电池,下面是一些其它测试框架无法做到的事情:

时间旅行: Cypress 在你运行测试的时候拍摄快照。 只要将鼠标悬停在 命令日志 上就能够清楚的了解到每一步发生了什么。
可调式能力: 你再也不需要去猜测测试为什么失败了。 调试工具 和 Chrome 的调试工具差不多。 清晰的错误原因和堆栈跟踪让调试能够更加快速。
自动等待: 在你的测试中不再需要添加等待或睡眠函数了。在执行下一条命令或断言前 Cypress 会 自动等待 异步将不再是问题.
Spies, Stubs, and Clocks: 验证和 控制 函数、服务器响应或者计时器的行为。你喜欢的单元测试的功能都掌握在你的手中。
网络流量控制: 非常容易的进行 控制、保存和边缘测试,而这并不需要涉及到你的服务。你可以根据需要保留网络流量。
一致的结果: 架构不需要 Selenium 或者 WebDriver。向快速,一致和可靠的无侵入测试看齐。
屏幕截图和视频: 可以查看测试失败时候系统自动截取的图片,或者整个测试的录制视频。

环境搭建

安装非常简单:

$ npm install cypress --save-dev

配置文件修改:

// baseUrl: "http://localhost:8080", // 测试域名
fixturesFolder: 'tests/e2e/fixtures', // 外部静态数据,如网络请求或存放模拟上传或读取的文件
integrationFolder: 'tests/e2e/specs', // 测试用例文件夹
screenshotsFolder: 'tests/e2e/screenshots', // 屏幕快照
// videoRecording: true,
videosFolder: 'tests/e2e/videos', // 录制后的文件夹
supportFile: 'tests/e2e/support/index.js', // 配置自定义命令全局注入
viewportHeight: 768, // 测试浏览器视口高度
viewportWidth: 1366 // 测试浏览器视口宽度

然后,可以将命令写到 package.json 中,如果使用 vue-cli,可以看到已经存在 “test:e2e”: “vue-cli-service test:e2e”,直接执行即可启动测试,在这之前需要先启动项目和 mock 服务。

$ npm run test:e2e

断言

在 Cypress 中有两种断言写法:

隐式: 使用 .should() 或者 .and(),.and() 只是 .should() 的别名,它链接多个断言使代码更易读
显式: 使用 expect

// 隐式
cy.get('#header a')
  .should('have.class', 'active')
  .and('have.attr', 'href', '/users')
// 显式
cy.get('tbody tr:first').should(($tr) => {
    
    
  expect($tr).to.have.class('active')
  expect($tr).to.have.attr('href', '/users')
})

// 常用断言
cy.get(':checkbox').should('be.disabled')
cy.get('form').should('have.class', 'form-horizontal')
cy.get('input').should('not.have.value', 'US')
cy.request('/users/1').its('body').should('deep.eq', {
    
     name: 'Jane' })

// 默认断言
/*
cy.visit() 预期这个页面是状态为200的 text/html内容页
cy.request() 预期远程服务器存在并提供响应
cy.contains() 预期包含内容的元素最终存在于DOM中
cy.get() 预期元素最终存在于 DOM中
.find() 预期元素最终存在于 DOM 中
.type() 预期元素最终为 可输入 状态
.click() 预期元素最终为 可操作 状态
.its() 预期最终找到当前主题的一个属性

截屏和视频录制

屏幕录制截屏是 Cypress 的一大特色,在 Test Runner 中单击项目的 Runs 选项卡,登录账号,再根据提示执行指令,即可完成屏幕录制和自动截屏。

$ ./node_modules/cypress/bin/cypress run --record --key xxxxxxxx

还可以在用例中主动截屏,存储在 screenshots 目录下。

jest 安装

jest 需要自动运行测试脚本,node 环境是必不可少的,如果从头搭建,首先得初始化项目 package.json 并安装 jest:

$ npm init
$ npm install jest -D

jest 默认不支持 es6,需要使用 babel 来支持 es6,安装 babel:

$ npm install @babel/core @babel/preset-env -D

配置 babel,修改 .babelrc 文件:

{
    
    
    "presets": [
        ["@babel/preset-env", {
    
    
            "targets": {
    
    
                "node": "current"
            }
        }]
    ]
}

vue-cli 中使用 jest

​ 现实项目中,往往不会从零搭建 jest 项目,更多的情况是,需要在一个脚手架已经搭建好的项目中引入自动化测试,此处在 vue-cli 基础上修改 jest 配置,安装好 jest 后需要修改项目根目录下的配置文件 jest.config.js,重点关注 testMatch 和 testPathIgnorePatterns 两个属性,testMatch 指定了匹配的测试用例文件的路径,而 testPathIgnorePatterns 则可以忽略指定文件,因此使用两个属性可以精确匹配到项目中所有的测试用例。

module.exports = {
    
    
  moduleFileExtensions: [
    'js',
    'jsx',
    'json',
    'vue'
  ],
  transform: {
    
    
    '^.+\\.vue$': 'vue-jest',
    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
    '^.+\\.jsx?$': 'babel-jest'
  },
  collectCoverageFrom: ['**/*.{vue}', '!**/node_modules/**'],
  transformIgnorePatterns: [
    '/node_modules/'
  ],
  moduleNameMapper: {
    
    
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  snapshotSerializers: [
    'jest-serializer-vue'
  ],
  testMatch: [
    '**/__tests__/unit/*.test.(js|jsx|ts|tsx)'
  ],
  testPathIgnorePatterns: [
    '/.eslintrc/.js'
  ],
  testURL: 'http://localhost/',
  watchPlugins: [
    'jest-watch-typeahead/filename',
    'jest-watch-typeahead/testname'
  ]
}

最后还需要在 package.json 中添加测试指令

{
    
    
	"test:unit": "vue-cli-service test:unit --watch"
}

执行对应指令即可在项目中执行测试

$ npm run test:unit

项目目录结构
项目的目录结构组织如下:

├── src
│   ├── assets
│   ├── containers
│   │   └── TodoList    
│   │       ├── __mocks__                测试mocks文件
│   │       ├── __tests__                测试用例文件
│   │       │   ├── unit                 单元测试
│   │       │   │   └── TodoList.test.js
│   │       │   └── integration          集成测试
│   │       │       └── store.test.js
│   │       ├── components               子组件
│   │       │   ├── Header.vue
│   │       │   └── UndoList.vue
│   │       │   
│   │       └── TodoList.vue             TodoList父vue组件
│   │
│   ├── utils
│   │   └── testUtils.js                 存放测试工具公共工具
│   ├── App.vue                          vue-App
│   └── main.js                          入口文件
│
├── public
├── jest.config.js                       jest配置文件
├── ...
└── package.json

jest基础测试知识

jest 文件和目录命名规范

待测试文件: hello.js 测试脚本文件取名:hello.test.jsorhello.spec.js 测试目录:testsor__tests__

测试函数

test("测试用列描述信息",()=>{
    
    

})
// or
it("测试用例描述信息",()=>{
    
    

})

断言函数

测试即运行结果是否与我们预期结果一致 断言函数用来验证结果是否正确

exspect(运行结果).toBe(期望的结果);
//常见断言方法
expect({
    
    a:1}).toBe({
    
    a:1})//判断两个对象是否相等
expect(1).not.toBe(2)//判断不等
expect({
    
     a: 1, foo: {
    
     b: 2 } }).toEqual({
    
     a: 1, foo: {
    
     b: 2 } })
expect(n).toBeNull(); //判断是否为null
expect(n).toBeUndefined(); //判断是否为undefined
expect(n).toBeDefined(); //判断结果与toBeUndefined相反
expect(n).toBeTruthy(); //判断结果为true
expect(n).toBeFalsy(); //判断结果为false
expect(value).toBeGreaterThan(3); //大于3
expect(value).toBeGreaterThanOrEqual(3.5); //大于等于3.5
expect(value).toBeLessThan(5); //小于5
expect(value).toBeLessThanOrEqual(4.5); //小于等于4.5
expect(value).toBeCloseTo(0.3); // 浮点数判断相等
expect('Christoph').toMatch(/stop/); //正则表达式判断
expect(['one','two']).toContain('one'); //不解释

分组函数

describe("关于每个功能或某个组件的单元测试",()=>{
    
    
    // 不同用例的单元测试
})

常见命令

{
    
    
  "nocache": "jest --no-cache", //清除缓存
  "watch": "jest --watchAll", //实时监听
  "coverage": "jest --coverage",  //生成覆盖测试文档
  "verbose": "npx jest --verbose" //显示测试描述
}

Jest 钩子函数

  • beforeAll:在所有测试用例执行前调用

  • afterAll:在所有测试用例执行后调用

  • beforeEach:在每一个测试用例执行前调用

  • afterEach:在每一个测试用例执行后调用

describe: 分组测试

每个describe都有自己的作用域且互不影响。

对于其执行的逻辑一定要放在钩子函数中

Jest 中的 Mock

生成mock函数

const func = jest.fn()

mock 函数作用

1、 捕获函数的调用和返回结果,以及this和调用顺序

2、 可以让我们自由的设置返回结果

3、 改变函数的内部实现

API:

mockReturnValue

mockReturnValueOnce

mockImplementation

mockImplementationOnce

mockResolvedValue

mockResolvedValueOnce

mockReturnThis

jest.mock 发现 util 是一个类,会自动把类的构造函数和方法变成 jest.fn()

mock的输出格式

对于单元测试,外部 class 的实现无需关心,使用 jest.fn 生成一个 mock 类,例如测试 mock.js

输出的 mock 为:

{
    
     
    calls: [ [ 123 ], [ 123 ], [ 123 ] ],
    instances: [ undefined, undefined, undefined ],
    invocationCallOrder: [ 1, 2, 3 ],
    results: [ 
        {
    
     type: 'return', value: 456 },
        {
    
     type: 'return', value: 789 },
        {
    
     type: 'return', value: undefined } 
    ]
}

jest基础测试

对象等值测试

describe('对象测试', () => {
    
    

    it("是否同一个对象", () => {
    
    
        const foo = {
    
     a: 1 }
        expect(foo).toBe(foo)
    })

    it("对象值是否相等", () => {
    
    
        expect({
    
     a: 1, foo: {
    
     b: 2 } }).toEqual({
    
     a: 1, foo: {
    
     b: 2 } })
    })

    test('对象赋值', () => {
    
    
        const data = {
    
     one: 1 };
        data['two'] = 2;
        expect(data).toEqual({
    
     one: 1, two: 2 });
    });

});

异步测试

异步测试脚本执行完,单元测试就结束了,如果需要延时才能断言的结果,单元测试函数需要设置 done 形参,在定时回调函数中调用,显示的通过单元测试已完成。

describe('异步操作测试',  () => {
    
    
    function foo(callback) {
    
    
        console.log('foo...')
        setTimeout(() => {
    
    
            callback && callback();
        }, 1000)
    }
    it('异步测试', (done) => {
    
    
        function bar() {
    
    
            console.log('bar..')
            done();
        }
        foo(bar);
    });
});

定时器测试(异步测试)及断言

基于 jest 提供的两个方法 jest.useFakeTimersjest.runAllTimers 可以更优雅的对延时功能的测试。

jest.useFakeTimers()避免定时器等待时间

jest.runAllTimers() 执行所有的timers

jest.runOnlyPendingTimers() 只执行当前队列中的timers

jest.advanceTimersByTime(msToRun) 时间快进多少

describe('定时器相关测试', () => {
    
    
    // 开启定时函数模拟
    jest.useFakeTimers();

    function foo(callback) {
    
    
        console.log('foo...')
        setTimeout(() => {
    
    
            callback && callback();
        }, 1000)
    }
    it('断言异步测试', () => {
    
    
        //创建mock函数,用于断言函数被执行或是执行次数的判断
        const callback = jest.fn();
        foo(callback);
        expect(callback).not.toBeCalled();
        //快进,使所有定时器回调
        jest.runAllTimers();
        expect(callback).toBeCalled();
    })
});

Dom 测试

测试 DOM 要记得把测试环境设为浏览器环境,jest 在底层模拟了一套 dom api

jest 提供了一套 node 环境下的 dom,在获取到指定的 dom 元素后,可以对 dom 元素执行各种操作:

const input = findTestWrapper(wrapper, ‘input’)
input.exists() // 获取dom存在性
input.setValue(‘csxiaoyao’) // 给dom赋值
input.trigger(‘keyup.enter’) // 触发dom方法
input.trigger(‘change’) // 触发dom方法

实现 dom 渲染测试,以及点击事件等交互功能测试。

describe('Dom测试', () => {
    
    
    it('测试按钮是否被渲染 ', () => {
    
    
        document.body.innerHTML = `
    <div>
        <button id='btn'>小按钮</button>
    </div> `
        console.log(document.getElementById('btn'), document.getElementById('btn').toString())
        expect(document.getElementById('btn')).not.toBeNull();
        expect(document.getElementById('btn').toString()).toBe("[object HTMLButtonElement]");
    });

    it('测试点击事件', () => {
    
    
        const onclick = jest.fn();
        document.body.innerHTML = `
        <div>
            <button id='btn'>小按钮</button>
        </div> `
        const btn = document.getElementById('btn');
        expect(onclick).not.toBeCalled();
        btn.onclick = onclick;
        btn.click();
        expect(onclick).toBeCalled();
        expect(onclick).toHaveBeenCalledTimes(1);
        btn.click();
        btn.click();
        expect(onclick).toHaveBeenCalledTimes(3);
    });
});

snapshot 快照测试

  • toMatchSnapshot 生成快照文件夹__snapshots__

  • toMatchInlineSnapshot 行内快照

  • 对于变化的配置量 Snapshot({ time2: expect.any(Date)})

  • 同样可以接收 Date | String | Number

test('should generateAnotherConfig 函数', () => {
    
    

  expect(generateAnotherConfig()).toMatchSnapshot({
    
    

    time2: expect.any(Date)

  })

})

Vue 测试

安装 unit-jest

​ 如果创建的项目没有安装 unit-jest 依赖包,可以通过 vue add @vue/unit-jest 命令添加。否则通过脚手架手动模式创建一个包含 unit-jest 的项目。

基础知识

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

不同的是,mount 方法会渲染完整的组件,包括子组件,适合 BDD 和集成测试,而 shallowMount 方法只会渲染当前组件,因此速度更快,效率更高,更加适合 TDD 和单元测试。

mount 和 shallowMount 的区别 - shallowMount 只挂载指定组件,不挂载子组件 - mount 挂载所有组件

为了方便获取测试需要的 DOM 元素,可以将获取 DOM 元素的方法进行封装,在 testUtils.js 中定义 findTestWrapper 方法如下:

export const findTestWrapper = (wrapper, tag) => {
    
    
	return wrapper.find(`[data-test="${
      
      tag}"]`)
}

Vue 的渲染机制 默认情况下 Vue 会异步地批量执行更新 (在下一轮 tick),以避免不必要的 DOM 重绘或者是观察者计算

异步测试需要在 nextTick () 之后执行

hello Jest Vue

vue 组件渲染测试

it('挂载countBtn组件', () => {
    
    
        const wraper = shallowMount(CountBtn);
        const btn = wraper.find("button");
        expect(wraper.html()).toBe(`<button>点击次数0</button>`);
    });

vm 实例测试

大部分的自动化测试,都是通过 vm 实例上的 data 变化来测试的,可以获取对应的 data 值,也可以通过 vm 调用相关方法。

wrapper.vm. d a t a . i n p u t V a l u e / / 获 取 i n p u t V a l u e w r a p p e r . v m . a d d U n d o I t e m ( ′ c s x i a o y a o ′ ) / / 执 行 a d d U n d o I t e m 方 法 w r a p p e r . v m . data.inputValue // 获取 inputValue wrapper.vm.addUndoItem('csxiaoyao') // 执行 addUndoItem 方法 wrapper.vm. data.inputValue//inputValuewrapper.vm.addUndoItem(csxiaoyao)//addUndoItemwrapper.vm.emit(‘add’, content) // 触发外部 add 方法

事件测试

vue 组件点击事件测试

it('测试countBtn组件点击', (done) => {
    
    
    const wraper = shallowMount(CountBtn);
    const btn = wraper.find("button");
    expect(wraper.html()).toBe(`<button>点击次数0</button>`);
    btn.trigger('click');
    setTimeout(() => {
    
    
        expect(wraper.html()).toBe(`<button>点击次数1</button>`);
        done();
    }, 1000);
});

it('优雅的测试点击事件', async () => {
    
    
    const wraper = shallowMount(CountBtn);
    const btn = wraper.find("button");
    expect(wraper.html()).toBe(`<button>点击次数0</button>`);
    btn.trigger('click');
    await wraper.vm.$nextTick();
    expect(wraper.html()).toBe(`<button>点击次数1</button>`);
});

异步操作

定时器测试,可以借助 vm.$nextTick 方法和 jest 定时器操作实现

beforeEach(() => {
    
    
	jest.useFakeTimers()
})
it(`
    1. 用户进入页面时,等待 3s
    2. 列表应该展示远程返回的数据
       `, (done) => {
    
    
       const wrapper = mount(TodoList, {
    
     store }) // 传入 store
       jest.runAllTimers()
       wrapper.vm.$nextTick(() => {
    
    
       	const listItems = findTestWrapper(wrapper, 'list-item')
       	// 不能直接判断,因为异步操作在 mounted 之后
       	expect(listItems.length).toBe(2)
       	done()
       })
       })

axios 异步测试

本地去模拟axios请求

实现方法

1.直接 使用创建的 __mock__ 模拟

2.打开jest.config.js Line 6 automock: true

模拟异步请示,测试渲染结果是否一致

<!-- User.vue -->
<template>
<table>
    <tr v-for="item in list" :key="item.id">
        <td>{
   
   {item.id}}</td>
        <td>{
   
   {item.name}}</td>
        <td>{
   
   {item.age}}</td>
    </tr>
</table>
</template>

<script>
export default {
      
      
    data() {
      
      
        return {
      
      
            list: []
        }
    },
    created() {
      
      
        this.$http.get('/user').then(({
       
       
            data
        }) => {
      
      
            this.list = data
        })
    }
}
</script>
// User.spec.js
import { mount } from '@vue/test-utils';
import User from '@/components/User';

it('测试用户组件', async() => {
    const wrapper = mount(User,{
        mocks:{
            $http:{
                get: url=>Promise.resolve({data:[{id:1,name:'xxxx',age:18},{id:2,name:'yyyy',age:19}]})
            }
        }
    })
    console.log(wrapper.html())
    // 渲染前
    expect(wrapper.html()).toBe('<table></table>');
		//对于异步请求,可以使用 vue 的 vm.$nextTick 方法实现异步数据的渲染。
    await wrapper.vm.$nextTick();
    // 渲染后
    // console.log(wrapper.html())
    // console.log(wrapper.find('tr'))
    expect(wrapper.findAll('tr').length).toBe(2)
    expect(wrapper.findAll('td').at(2).html()).toBe('<td>18</td>')

});

猜你喜欢

转载自blog.csdn.net/weixin_46232841/article/details/117528453