写点什么

前端自动化测试详解(下)

2019 年 9 月 26 日

前端自动化测试详解(下)

5.4 测试方案代码

下面开始梳理完整的自动化测试方案,整体目录结构如下:



自动化测试方案项目结构


5.4.1 单元测试


(1)对如下方法进行单元测试


1// /src/client/common/js/testUtil.js2export const sum = (a, b) => {3  return a + b4}
复制代码


编写好测试用例


 1import { sum } from '../../src/client/common/js/testUtil.js' 2const { expect } = require('chai') 3 4describe('单元测试: sum (a, b)', function () { 5  it('1+1 应该等于 2', function () { 6    expect(sum(1, 1)).to.be.equal(2) 7  }) 8}) 9// skip可以指定跳过某个分组10describe.skip('单元测试:金额按千分位逗号分隔的方法 formatMoney (s, type)', function () {...})
复制代码


然后使用 mocha 执行测试用例,输出结果如下



单元测试-公共方法


可以看到两个测试分组有一个测试通过,一个被我们主动跳过。使用mocha执行测试用例时,因为我们指定了测试报告格式--reporter参数为mochawesome,测试报告会被输出为如下的html格式



单元测试报告


为了分析当前测试用例对源代码的覆盖情况,我们使用Istanbul生成测试覆盖率报告



单元测试代码覆盖率报告


代码覆盖率有四个测量维度:


语句覆盖率(statement coverage):是否每个语句都执行了


分支覆盖率(branch coverage):是否每个 if 代码块都执行了


函数覆盖率(function coverage):是否每个函数都调用了


行覆盖率(line coverage):是否每一行都执行了


分别对应上图的 Statements、Branches、Functions、Lines,点击左侧链接可以查看源码测试详情,绿色部分表示已被测试覆盖



代码覆盖详情


关于测试覆盖率,需要强调的是,我们不应该把测试覆盖率的高低作为检验项目质量的标准,只能作为参考。代码覆盖率真正的意义在于帮助开发者找到代码设计的问题,帮助我们发现为什么有的代码没有被测试覆盖到,是代码设计有问题,还是加入了无用代码,它可以指导我们在代码设计中做更好的抽象,写可测试的代码。


(2)React 组件测试


现在有如下的 React 组件


 1// /src/client/components/Empty/index.jsx' 2import React, { Component } from 'react' 3import { Icon } from 'antd' 4 5const Empty = (props) => { 6  const placeholder = props.placeholder 7 8  return ( 9    <div>10      <Icon type='meh-o' />11      <span>{placeholder || '数据为空'}</span>12    </div>13  )14}1516module.exports = Empty17
复制代码


编写测试用例对它进行测试


 1import React from 'react' 2import { expect } from 'chai' 3import Enzyme, { mount, render, shallow } from 'enzyme' 4import Adapter from 'enzyme-adapter-react-15.4' // 根据React的版本安装适配器 5import Empty from '../../src/client/components/Empty/index.jsx' 6import { spy } from 'sinon' // 对原有的函数进行封装并进行监听 7 8Enzyme.configure({ adapter: new Adapter() }) // 使用Enzyme 先适配React对应的版本 910describe('测试React组件: <Empty />', () => {11  it('不传入属性时,组件中span的文本为"数据为空"', () => {12    const wrapper = render(<Empty />)13    expect(wrapper.find('span').text()).to.equal('数据为空')14  })1516  it('传入属性"我是占位文本"时,组件中span的文本为"我是占位文本"', () => {17    const wrapper = render(<Empty placeholder='我是占位文本' />)18    expect(wrapper.find('span').text()).to.equal('我是占位文本')19  })20})
复制代码


使用 mocha 执行测试用例会生成如下测试报告,测试通过



React 组件测试报告


测试覆盖率报告如下



React 组件测试覆盖率


!



React 组件代码覆盖详情


5.4.2 接口测试


编写测试用例,使用 supertest 实施接口测试


 1const request = require('supertest') 2const { expect } = require('chai') 3const BASE_URL = 'http://127.0.0.1:1990' 4 5describe('接口测试:商户登录测试用例', function () { 6  it('登录接口 /api/user/login', function (done) { 7    request(BASE_URL) 8      .post('/api/user/login') 9      .set('Content-Type', 'application/json') // set header内容10      .send({ // send body内容11        user_code: 666666,12        password: 66666613      })14      .expect(200) // 断言希望得到返回http状态码15      .end(function (err, res) {16        // console.info(res.body) // 返回结果17        expect(res.body).to.be.an('object')18        expect(res.body.data.user_name).to.equal('商户AAAAA')19        done()20      })21  })22})
复制代码


执行接口测试用例生成如下测试报告



接口测试报告



接口测试报告


5.4.3 e2e测试


编写 e2e 测试用例,使用 selenium-webdriver 驱动浏览器进行功能测试


 1const { expect } = require('chai') 2const { Builder, By, Key, until } = require('selenium-webdriver') 3const chromeDriver = require('selenium-webdriver/chrome') 4const assert = require('assert') 5 6describe('e2e测试:商户系统端到端测试用例', () => { 7  let driver 8  before(function () { 9    // 在本区块的所有测试用例之前执行10    driver = new Builder()11      .forBrowser('chrome')12      // 设置无界面测试13      // .setChromeOptions(new chromeDriver.Options().addArguments(['headless']))14      .build()15  })1617  describe.skip('登录相关传统用例-跳过', function () {...})1819  describe('登录商户系统', function () {20    this.timeout(50000)21    it('登录跳转', async () => {22      await driver.get('http://dev.company.home.ke.com:1990/login') // 打开商户登录页面23      await driver.findElement(By.xpath('//*[@id="root"]/div/div[2]/div/ul/li[1]/input')).sendKeys(666666) // 输入用户名24      await driver.findElement(By.xpath('//*[@id="root"]/div/div[2]/div/ul/li[2]/input')).sendKeys(666666) // 输入密码25      await driver.findElement(By.xpath('//*[@id="root"]/div/div[2]/div/div/button')).click() // 点击登录按钮26      const currentTitle = await driver.getTitle()27      await driver.sleep(2000)28      expect(currentTitle).to.equal('商户管理系统')29    })30  })3132  after(() => {33    // 在本区块的所有测试用例之后执行34    driver.quit()35  })36})
复制代码


使用 mocha 执行 e2e 测试用例生成如下测试报告



e2e 测试报告


下图是selenium-webdriver驱动chrome浏览器自动运行,进行功能测试



e2e 测试效果



驱动浏览器执行测试任务


5.4.4 基准测试


假设当前需要测试正则表达式的 test 方法和字符串的 indexOf 方法的性能,我们通常会采用如下方法进行测试:让两个方法分别执行 1000 次,比较哪个耗时长。


 1// 判断某个字符串中是否存在特定字符,比较reg.test和str.indexOf性能 2const testPerf = (count) => { 3  var now = new Date() - 1 4  var i = count 5  while (i--) { 6    /o/.test('Hello World!') 7  } 8  console.log(`test方法执行${count}次用时`, new Date() - 1 - now) 9}1011const indexOfPerf = (count) => {12  var now = new Date() - 113  var i = count14  while (i--) {15    'Hello World!'.indexOf('o') > -116  }17  console.log(`indexOf方法执行${count}次用时`, new Date() - 1 - now)18}1920testPerf(1000)21indexOfPerf(1000)
复制代码


测试结果如下,因为代码执行较快,两个方法执行 1000 次的时间都为零,无法准确判断代码执行效率



方法性能对比


科学的统计方法是需要多次执行,对大量的执行结果进行采样,我们可以使用工具帮我们完成这件事,如下使用benchmark进行测试``` 1// 判断某个字符串中是否存在特定字符,比较reg.test和str.indexOf性能 2const Benchmark = require('benchmark') 3const suite = new Benchmark.Suite() 4 5// add test 6suite.add('正则表达式test方法', function () { 7 /o/.test('Hello World!') 8}) 9 .add('字符串indexOf方法', function () {10 'Hello World!'.indexOf('o') > -111 })12 // add listeners13 .on('cycle', function (event) {14 console.log(String(event.target))15 })16 .on('complete', function () {17 console.log('Fastest is ' + this.filter('fastest').map('name'))18 })19 // run async20 .run({ 'async': true })```执行测试代码,结果如下,indexOf每秒执行的次数比test每秒执行的次数超出了一个数量级,所以indexOf性能更好



基准测试结果


6 总结

梳理完单元测试、接口测试、功能测试、基准测试的具体实施方案后,结合自动化测试的特点我们可以得出以下结论:


前端要不要进行自动化测试,需要根据具体的项目特点进行判断,对于满足以下条件的代码可以进行自动化测试:


  • 核心功能模块、函数

  • 短期不会发生变化的 UI 组件

  • 提供外部调用的接口

  • 对方法性能进行基准测试


最后,要强调一点,我们的目标是保证代码健壮、可维护,提高开发效率,自动化测试只是一种手段。


作者介绍:


扣丁(企业代号名),目前负责贝壳找房装修平台 B 端业务研发工作。


本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。


原文链接:


https://mp.weixin.qq.com/s/pwSzGhRM_25GTjKd2aRt1Q


2019 年 9 月 26 日 23:451022

评论 1 条评论

发布
用户头像
比上更有看头,实例很香
2020 年 01 月 05 日 10:34
回复
没有更多了
发现更多内容

长文图解:金字塔思维如何指导技术系统优化

JAVA前线

性能优化 金字塔 结构化思维

模块一作业

架构0期-Bingo

TEMS模型--衡量你的人生资源

俞凡

认知

第二周作业-熊猫潘戈项目利益相关方

小夏

产品经理训练营 邱岳

零基础入门网络渗透到底要怎么学?

Machine Gun

网络安全 信息安全 渗透测试 内网渗透 内网

网络攻防学习笔记 Day70

穿过生命散发芬芳

网络攻防 7月日更

架构实战营 - 模块 8- 作业

请弄脏我的身体

架构实战营

模块8作业

dwade

#架构实战营

图解DDD建模六个问题与六个步骤

JAVA前线

Java 领域驱动设计 DDD

公司内部使用的数仓开发规范

白程序员的自习室

数据仓库 开发规范 数仓规范 7 月日更

暑假期间快手将重点整治平台:短视频平台如何完善内容审核机制

石头IT视角

Kats-Facebook最新开源的时序分析工具

好孩子

从明天起开始认真更新了

IT蜗壳-Tango

7 月日更

业务架构模块8作业:设计消息队列存储消息数据的MySQL 表格

好吃不贵

记录一次Neokylin_Server_V5系统已有分区的扩容操作

星河寒水

分区扩容

架构训练营模块 1 作业 - 1班助教

Geek_97d901

架构实战营 模块八课后作业

iProcess

架构实战营

【LeetCode】基于时间的键值存储Java题解

HQ数字卡

算法 LeetCode 7月日更

Linux之find命令的参数详解

入门小站

Linux

在线脑图思维导图生成工具

入门小站

工具

Go 学习笔记之 Map

架构精进之路

go 7 月日更

直接上干货!这些细节在Android面试上要注意了

欢喜学安卓

android 程序员 面试 移动开发

正式加入字节跳动!如何才能更容易拿到大厂Offer

欢喜学安卓

android 程序员 面试 移动开发

ACM金牌选手整理的【LeetCode刷题顺序】

编程熊

Java 面试 算法 面经 笔试

推荐系统的未来发展(三十三)

数据与智能

价值观 推荐系统

分布式ID生成方案选型!详细解析雪花算法Snowflake

攻城狮Chova

分布式 雪花算法 7 月日更

Ta想做一粒智慧的种子

白洞计划

我为什么要学习业务建模?

escray

极客时间 学习笔记 七月日更 如何落地业务建模

全面了解Java并发编程基础!超详细!

程序员的时光

Java 并发编程

图像的模板匹配,Python OpenCV 取经之旅第 29 天

梦想橡皮擦

7月日更

我赌一包辣条这是全网最详细的代码审计(没有之一)

网络安全学海

黑客 网络安全 信息安全 代码审计 漏洞分析

前端自动化测试详解(下)-InfoQ