最近几个月来,笔者一直在探索前端UI自动化测试的场景和方案。最初的时候,面对众多的技术选型,显得有些茫然,而团队此前也没有太多关于这方面的经验,只能一步一步摸索总结,固然期间也踩过很多坑,最终造成了一套相对稳定的测试方案,将来还将继续扩展和完善。前端
项目地址: jest-puppeteer-testing
在这篇文章中,笔者想和你们分享一下本身对于UI自动化测试的思考和经验。git
业务的更新迭代频繁,传统测试大部分都仍是手工、肉眼的模式来进行,没法知足产品敏捷开发、快速迭代的需求。而UI自动化能让全功能的回归变得简单,释放纯手工测试的人力资源,而且回归测试可以覆盖到全部的逻辑场景,这对测试的效率,以及整个开发流程的效率都是很大的提高,而且可以规避不少人的主观和客观因素致使的漏测或者疏忽。github
其余测试方式的局限性:api
单元测试(Unit Testing)服务器
事实上,单元测试确实可以帮助咱们发现大部分的问题,可是在复杂的前端交互中,单纯的单元测试并不能真实地反映用户操做的路径,而单元测试通常的场景是测试一系列的功能集合。网络
快照测试(Snapshot Testing)框架
DOM结构并不能彻底反映页面的视觉效果,DOM结构不变并不彻底等于样式不变。此外,大多数工具都是React专用,非React应用基本不支持。async
笔者想说:工具
不少人认为,UI老是频繁的变更,致使测试用例维护成本高,性价比低,所以UI自动化测试比较适合场景稳定的业务。其实不是,这里的UI不只仅指的是视觉,更多的是业务逻辑。UI能够多变,但业务逻辑必定是趋于稳定的,尤为是核心业务,想想用户得多辛苦才能适应这种业务逻辑频繁变动的产品啊。
TypeScript + Jest + Puppeteer
事实上,对于UI自动化测试来讲,许多框架之间并无太多差异,也历来不是影响整套测试用例是否健壮的关键性因素。相比之下,如何提升测试用例稳定性及全面性才是让UI自动化测试方案落地的重要细节。单元测试
你们能够参考jest-puppeteer-testing,这里再也不累述。
// setup/expect-image-snapshot.ts // 让jest支持保存/比对屏幕截图 import { configureToMatchImageSnapshot } from 'jest-image-snapshot'; expect.extend({ toMatchImageSnapshot: configureToMatchImageSnapshot({ customSnapshotsDir: '__image_snapshots__', }), });
// setup/enhance-puppeteer.ts // 加强puppeteer功能,如:拦截请求并使用mock数据 import { onRequestInterceptor } from '../utils/request'; jest.setTimeout(30000); beforeAll(async () => { page.on('request', onRequestInterceptor); // 拦截请求,使用代理数据 await page.setRequestInterception(true); });
// utils/request.ts // mock数据的核心文件 // 这里只拦截xhr或fetch请求,固然你也能够自行扩展 import { URL } from 'url'; import { Request } from 'puppeteer'; import mocks from '../mocks'; // 设置请求拦截器的数据,用于同一请求返回不一样结果,生效一次后自动销毁 export const interceptors: { [api: string]: any } = {}; export const setRequestInterceptor = (api: string, value: any) => { interceptors[api] = value; }; export const onRequestInterceptor = (request: Request) => { const resourceType = request.resourceType(); if (resourceType === 'xhr' || resourceType === 'fetch') { const location = new URL(request.url()); const mockKey = location.pathname; if (mockKey && mocks.hasOwnProperty(mockKey)) { const mock = mocks[mockKey]; let response: any; if (typeof mock === 'function') { response = mock({ location, request, interceptor: interceptors[mockKey] }); delete interceptors[mockKey]; // 生效一次后自动销毁 } else { response = mock; } if (response) { if (response.body != null && typeof response.body === 'object') { response.body = JSON.stringify(response.body); } request.respond(response); } } else { request.continue(); } } else { request.continue(); } };
shared.d.ts
定义数据类型cases
目录下存放测试用例mocks
目录下存放mock数据utils
目录下存放工具方法补充说明:关于mock的类型定义,能够在shared.d.ts中找到,固然你也能够在这里增长其余类型定义
测试地址的选择(本地/线上)
尽可能抹平不肯定因素带来的影响
如维持数据请求的结果稳定,日期时间稳定,保证页面渲染的一致性。假如因为数据返回或时间的不肯定性,致使每次页面渲染不同,那这样测试也失去了意义。
尽可能明确保存屏幕截图的时机
如访问一个页面后截图,因为网络因素的缘由,图片资源并非每次都加载完成,从而致使截图先后不同。
......(暂时写这么多,有空再更)
事实上,这套UI自动化测试方案更像是端到端测试(E2E Testing),即模拟一个用户将程序做为一个彻底的黑盒,打开应用程序模拟输入,检查功能以及界面是否正确,配合屏幕截图能够直观感觉到用户进行某些交互产生的具像化视觉效果。
项目地址: jest-puppeteer-testing