基于jest和puppeteer的前端自动化测试实战

前端测试现状

经常听到后端同学说“单元测试”,前端写过测试用例的有多少?答案是:并不多,为什么呢?两个主要原因

1、前端属于GUI软件,浏览器众多,兼容问题让人头大,用户量有一定规模的浏览器包括:

  IE8、IE9、IE10、IE11、chrome、FireFox、360浏览器、搜狗浏览器、QQ浏览器……

要在这么多浏览器上做几轮测试并不容易

2、前端界面变化快,很多时候界面比测试脚本迭代的更快,测试跟不上脚步,投入产出不成正比

以上两点导致前端测试不受重视,很多前端开发者可能工作数年仍未写过单元测试,这是现状

该不该写前端测试,还是得视项目情况而定,一般标准的开源项目都会做单元测试,所以有必要了解一下前端测试大概是个什么东西


分类

前面一直说的是前端测试而不是单元测试,是因为前端不同于后端,前端是有界面的,测试应该分为单元测试和集成测试

所谓单元测试,就是测试一个函数或某个代码片段,通过模拟输入确保输出符合预期

实例1:以下是一个完整的测试用例,用来测试函数sum是否按预期的计算两个数字之和

const sum = (a,b) => {
    return a+b;
}
describe(‘分组测试描述‘,() => {
    test(‘test 1+1‘, () => {
        expect(1 + 1).toBe(2);
    });
})

解释一下两个关键字:

describe,作用是将test分组,影响 beforeEach/afterEach/beforeAll/afterAll四个方法的作用域,它有两个参数

第一个参数就是分组描述,描述这个分组是干嘛的

第二个参数是个回调函数,内部可以有多个test,test的作用是声明一个测试

test,作用就是声明一个测试,有三个参数

第一个同样是描述,描述测试内容

第二个也是回调,内部为详细测试内容

第三个是测试超时时间,默认为5s钟,做单元测试一般都是足够的,集成测试一般都是不够的,可以用jest.setTimeout(timeout)方法修改所有test默认超时时间

集成测试,测的是一个功能模块,比如用户注册功能,集成测试又包括UI测试,UI测试用于确保页面正常渲染

集成测试完全是用测试脚本去模拟用户操作,比如打开浏览器、点击注册链接、输入用户名密码、点击注册

UI测试怎么确保页面正常渲染?

两种方式:像素级对比和快照

像素及对比,就是首先人肉确认页面渲染正常,执行脚本对页面截个图,下次利用测试脚本截个图跟上次的截图的每个像素自动进行对比,如果每个像素都一样,那么测试通过

快照,这里的快照不是截图的意思,而是将页面渲染后的DOM结构生成一个序列化的文本,下次再次生成一个序列化的DOM文本与之对比,如果内容完全一样,测试通过,做快照测试,必须保证多次测试输出快照总是一致的,然而在react中,model经常变化,这时就要用mock模拟函数返回固定数据确保model不变,mock功能在下文有介绍


主流库

流行的单元测试包括jest、mocha、jasmine、……

流行的集成测试库包括puppeteer、casperJS、PhantomJS、……

jest的特点是零配置、即时反馈,它所有测试用例默认是并行执行的,速度快,它也可以配置成串行,在调试时比较有用,jest每个测试用例文件都是一个沙箱,在单个测试文件内部定义或修改全局变量,不会影响其它测试文件,jest由Facebook团队维护,对React友好,适合大型项目

mocha是一个精简而灵活的单元测试框架,它本身没有包含断言库和mock(模拟)功能,需要自行引入其它库,而jest和jasmine都自带断言库和mock功能,什么是mock,后面会介绍

puppeteer是个神器,它并不仅仅可以做自动化集成测试,它本身是个node库,自带chromuium浏览器(所以npm安装它比较慢),它提供了一些高级API通过DevTools协议控制headless chrome或chromuium,它也可以配置为使用有界面版的chrome,既然是浏览器,chrome能做到的它基本都能做到,chrome做不到的,它也能做到,用puppeteer做集成测试,测试用例是真正在真实的浏览器上执行的,下面几点都是它所擅长的

  • 生成页面屏幕截图或pdf
  • 自动提交表单,做UI测试、模拟键盘输入、鼠标操作等
  • 创建一个最新的自动化测试环境,用最新的JavaScript和浏览器功能,直接在最新的chrome中做测试
  • 捕获你网站的时间线跟踪,以帮助诊断性能问题

casperJS是一个基于PhantomJS的库,它封装了PhantomJS的API使它更容易使用,PhantomJS内置了webkit的内核,测试用例并不是跑在真正的浏览器上面

本文的重点是jest和puppeteer,下面是实例和API都是基于这两者


Setup

如果在执行jest测试用例之前需要做一些配置,在用例执行完做一些清除操作,那你需要了解下面4个API

beforeEach(callback)

在每个test用例执行前执行回调callback,在单个测试文件内,它对每个test都有效,如果它放在describe内部,那么它只对describe内部的test用例有效,上面讲过,describe内部可以有多个test

afterEach(callback)

在每个test用例执行后执行回调callback,作用域同beforeEach

beforeAll(callback)

在所有test用例执行前执行回调callback

afterAll(callback)

在所有test用例执行后执行回调callback


断言

在编写测试时,您经常需要检查值是否符合某些条件。Expect就是干这个的,它有很多 匹配方法,实例1中的

expect(sum(1,1)).toBe(2);

意思就是断言函数sum执行的结果等于2,其它匹配方法包括但不限于:

  • 判断某个变量是否定义:.toBeDefined();
  • 比较某个值是否大于指定数字:.toBeGreaterThan(number);
  • 检查对象length属性是否等于指定值:.toHaveLength(number);
  • ……

mock定时器

业务代码中经常会用到定时器,包括setTimeout、setInterval,在做单元测试的时候,如果傻傻地等定时器一秒一秒走那就很浪费时间,大家都是一秒钟几十万上下的人,哪怕几秒钟也不会浪费,jest的mock功能,可以模拟定时器执行,有4个重要的API必须了解一下:

  1. jest.useFakeTimers() 声明在当前测试文件中使用模拟定时器,声明后,可以直接用expect(setTimeout).toHaveBeenCalledTimes(1)判断定时器调用的次数
  2. jest.runAllTimers() 立即执行所有定时器
  3. jest.runOnlyPendingTimers() 立即执行挂起的定时器
  4. jest.advanceTimersByTime(msToRun) 提前msToTun毫秒执行定时器

第1个API需要注意,仅仅声明jest.useFakeTimers(),定时器回调的代码并不会执行,第2、3、4个API都会真正执行定时器回调代码;

jest.runOnlyPendingTimers()执行挂起的定时器是什么意思?其实就是即将要执行的那一个定时器,下面这段代码,会调用两次setTimeout,第一次是jest.useFakeTimers()触发的,第二次是jest.runOnlyPendingTimers()触发的

function timeout() {
    setTimeout(() => {
        console.count(‘count‘);
        timeout();
    }, 10000);
}

jest.useFakeTimers();

test(‘useFakeTimers‘, () => {
    timeout();
    jest.runOnlyPendingTimers();
    expect(setTimeout).toHaveBeenCalledTimes(2);
});

如果把上一段测试用例的jest.runOnlyPendingTimers()换成jest.runAllTimers()会进入死循环


mock函数

手动实现了一个forEach函数,要测试它是否按预期执行回调,这里模拟了一个回调函数mockCallback,模拟函数的好处是可以获取每次调用它的参数和它的执行次数,在项目中可以模拟请求返回指定数据而无需访问服务器

function forEach(items, callback) {
    for (let index = 0; index < items.length; index++) {
        callback(items[index]);
    }
}

test(‘test forEach‘, () => {
    const mockCallback = jest.fn();
    forEach([0, 1], mockCallback);
    // The mock function is called twice
    expect(mockCallback.mock.calls.length).toBe(2);
    // The first argument of the first call to the function was 0
    expect(mockCallback.mock.calls[0][0]).toBe(0);
    // The first argument of the second call to the function was 1
    expect(mockCallback.mock.calls[1][0]).toBe(1);
}); 

异步

测试脚本中可能包含异步操作,如果不用异步方式写test,test执行到最后一行就认为测试完成,很可能测试失败

方式一:done回调,传入参数done,异步操作执行完后执行done()

// done
test(‘async test‘, done => {
    function callback(data) {
        expect(data).toBe(‘xx‘);
        done();
    }
    fetchData(callback);
});

方式二:返回promise,test会等promise执行完才跳出

// return promise
test(‘async test‘, () => {
    //判断当前测试有一个断言被执行
    expect.assertions(1);
    return fetchData().then(data => {
        expect(data).toBe(‘xx‘);
    });
});

方式三:.resolves/.rejects,同样必须return promise

test(‘works with resolves‘, () => {
    expect.assertions(1);
    return expect(user.getUserName(5)).resolves.toEqual(‘xx‘);
});

方式四:ES8的async/await,可以和.resolves/.rejects混合使用

// async/await can be used.
it(‘works with async/await‘, async () => {
    expect.assertions(1);
    const data = await user.getUserName(4);
    expect(data).toEqual(‘xx‘);
});

// async/await can also be used with `.resolves`.
it(‘works with async/await and resolves‘, async () => {
    expect.assertions(1);
    await expect(user.getUserName(5)).resolves.toEqual(‘xx‘);
});

关于describe还有两个重要重要的方法应该了解下

describe.only(name, fn)

只执行该describe,其它describe会被忽略

describe.skip(name, fn)

和.only相反,只跳过该describe,在调试时很有用

puppeteer常用的几个API也了解一下

  • puppeteer.launch() 实例化一个浏览器
  • browser.newPage(url) 打开新页面
  • page.goto(url) 跳转到url
  • page.$(selector) 选择页面元素,返回的是元素句柄(ElementHandle),不是真实DOM节点,selector底层实现用的就是document.querySelector
  • page.$$(selector) 同上,selector底层实现用的就是document.querySelectorAll,返回多个句柄
  • page.$eval(selector, pageFunction[, ...args]) 同上,返回的是pageFunction的返回值,在pageFunction内可以获取到真实DOM节点,如获取元素ID,page.$eval(‘div‘, divs => divs.id);
  • page.$$eval(selector, pageFunction[, ...args]) 同上,selector底层实现用的就是document.querySelectorAll
  • page.click(selector[, options]) 点击指定元素
  • page.type(selector, text[, options]) 改变元素的值,如果是react,会同时改变model层数据,就像真实用户输入

单元测试 VS 集成测试

两种测试方法各有优缺点,具体用哪种视项目具体情况而定

原文地址:https://www.cnblogs.com/wangmeijian/p/9080188.html

时间: 2024-08-29 06:38:40

基于jest和puppeteer的前端自动化测试实战的相关文章

web前端自动化测试利器puppeteer介绍

web前端自动化测试利器puppeteer介绍 Intro Chrome59(linux.macos). Chrome60(windows)之后,Chrome自带headless(无界面)模式很方便做自动化测试或者爬虫.但是如何和headless模式的Chrome交互则是一个问题.通过启动Chrome时的命令行参数仅能实现简易的启动时初始化操作.Selenium.Webdriver等是一种解决方案,但是往往依赖众多,不够扁平. Puppeteer是谷歌官方出品的一个通过DevTools协议控制h

基于Storm构建实时热力分布项目实战

详情请交流  QQ  709639943 01.基于Storm构建实时热力分布项目实战 02.以慕课网日志分析为例 进入大数据 Spark SQL 的世界 03.Spring Cloud微服务实战视频课程 04.漫谈spring cloud 与 spring boot 基础架构 05.Java秒杀系统方案优化 高性能高并发实战 06.Java深入微服务原理改造房产销售平台 07.快速上手Linux 玩转典型应用 08.漫谈spring cloud分布式服务架构 09.Java Spring Se

Selenium2+Python自动化测试实战

本人在网上查找了很多做自动化的教程和实例,偶然的一个机会接触到了selenium,觉得非常好用.后来就在网上查阅各种selenium的教程,但是网上的东西真的是太多了,以至于很多东西参考完后无法系统的学习和应用,有一次在网上随意搜索,找到了-虫师-写的<Selenium2自动化测试实战基于Python语言>,觉得真心不错,内容也很调理,为了方便自己学习和知识的整理,就把其中虫师编写的自动化项目教程整理一下,有兴趣的可以去参看虫师的博客http://www.cnblogs.com/fnng/ 以

基于AngularJS的企业软件前端架构[转载]

这篇是我参加QCon北京2014的演讲内容: 提纲: 企业应用在软件行业中占有很大的比重,而这类软件多数现在也都采用B/S的模式开发,在这个日新月异的时代,它们的前端开发技术找到了什么改进点呢? B/S企业软件前端开发模式大体上与桌面软件类似,都是偏重量级的,在前端可能会有较多的业务逻辑,这些业务逻辑如何被合理模块化,与界面分离,以便测试,成为这个领域的一个重要挑战.另一方面,由于企业应用的界面相对规整,偏重的是数据存取,没有太多花哨的东西,所以常见的界面控件也是可枚举的,如何让开发界面的工作能

前端自动化测试漫长路之——Selenium初探

引言 最近想解决前端开发或测试中的两个问题:一是界面UI的布局适配,能否在测试的过程中,通过命令操作真机打开相应页面然后截屏,通过对图片识别分类,发现有问题的图片,然后及时修复:二是页面性能分析,很多时候页面只能在指定的Webview中使用,能否直接通过命令打开指定的页面,分析页面在真实APP中的性能,并生成报告.这两个问题的前提就是通过命令直接操作手机App,带着问题找线索,于是我就结识了Selenium,下面将结合实例和大家分享一下. Selenium是什么? 先看一下官网的解释: Sele

关于《Python自动化测试实战》

作者有话说 笔者写这本书的初心是想通过自身经验分享一些在自动化测试领域中的实用技术,能够帮助那些正在从事自动化测试相关工作或者准备转型自动化测试的测试人员.任何一门技术涵盖的知识点都是非常广泛的,可能并不是一本书就能够写完并且讲解清楚.本书没有太多的废话,书中内容都是些实战型的例子,任何读者对着本书中的例子都可以很快上手学习.本书中实战教程属于一气呵成,从头到尾,知识体系版块布局清晰,实操流程表述细致且接地气,可以保证大部分读者在阅读本书时比较轻松.愉悦.并不会像有些书的内容读来读去,知识点分散

基于Hadoop离线大数据分析平台项目实战

基于Hadoop离线大数据分析平台项目实战  课程学习入口:http://www.xuetuwuyou.com/course/184 课程出自学途无忧网:http://www.xuetuwuyou.com 课程简介: 某购物电商网站数据分析平台,分为收集数据.数据分析和数据展示三大层面.其中数据分析主要依据大数据Hadoop生态系统常用组件进行处理,此项目真实的展现了大数据在企业中实际应用. 课程内容 (1)文件收集框架 Flume ①Flume 设计架构.原理(三大组件) ②Flume 初步使

移动前端重构实战系列

移动前端重构实战系列:5-7章 2016-07-16 07:16 (本文系来自腾讯imweb团队 结一大大 关于移动端重构经验以及思想的实战系列,推荐点击左下角的阅读原文.) ”本系列教程为实战教程,是自己移动端重构经验及思想的一次总结,也是对sheral UI的一次全方位剖析,首发在imweb和w3cplus两大站点及“前端Talk”微信公众号,其余所有标注或没有标注来源的均为转载.“ ——imweb 结一 5.Form form 大概要实现的效果如下图(具体demo可见sheral form

前端开发--实战篇之测试框架

如果不了解前端开发环境,请参考搭建前端开发环境, 如果不了解实战篇的项目配置,请参考前端开发--实战篇 步骤一:待命 在cmd里面,进入到public文件夹待命. 步骤二:初始化karma配置文件karma.conf.js 执行初始化配置文件的命令: karma init 根据向导,大多数使用默认配置即可.具体见下图: 步骤三:根据当前目录结构,修改配置文件karma.conf.js 添加待测试的js文件: files: [   'app/dist/lib/angular/*.js',   'a