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
评论 1 条评论