前端测试及相关工具介绍
测试的好处:
- 保障代码质量和功能的实现的完整度
- 提升开发效率,在开发过程中进行测试能让我们提前发现 bug ,此时进行问题定位和修复的速度自然比开发完再被叫去修 bug 要快许多
- 便于项目维护,后续任何代码更新也必须跑通测试用例,即使进行重构或开发人员发生变化也能保障预期功能的实现
当然,凡事都有两面性,好处虽然明显,却并不是所有的项目都值得引入测试框架,毕竟维护测试用例也是需要成本的。对于一些需求频繁变更、复用性较低的内容,比如活动页面,让开发专门抽出人力来写测试用例确实得不偿失。
而那些适合引入测试场景大概有这么几个:
- 需要长期维护的项目。它们需要测试来保障代码可维护性、功能的稳定性
- 较为稳定的项目、或项目中较为稳定的部分。给它们写测试用例,维护成本低
- 被多次复用的部分,比如一些通用组件和库函数。因为多处复用,更要保障质量
这里从单元测试和页面功能测试两个方面展开讨论
单元测试
对代码的模块测试,比如函数、接口等
单元测试又有不同的测试风格,主要有TDD,BDD
TDD
TDD是Test Driven Development 的缩写,也就是测试驱动开发。 TDD需要遵循如下规则:
- 写一个单元测试去描述程序的一个方面。
- 运行它应该会失败,因为程序还缺少这个特性。
- 为这个程序添加一些尽可能简单的代码保证测试通过。
- 重构这部分代码,直到代码没有重复、代码责任清晰并且结构简单。
- 持续重复这样做,积累代码。
但前端开发中,除了一些框架和库,愿意去写单测的少之又少。另外单测维护成本较高,而且也没法满足前端测试的所有需求。
BDD
从业务的角度定义具体的,以及可衡量的目标 找到一种可以达到设定目标的、对业务最重要的那些功能的方法 然后像故事一样描述出一个个具体可执行的行为。其描述方法基于一些通用词汇,这些词汇具有准确无误的表达能力和一致的含义。例如,expect, should, assert 寻找合适语言及方法,对行为进行实现 测试人员检验产品运行结果是否符合预期行为。最大程度的交付出符合用户期望的产品,避免表达不一致带来的问题
测试框架和断言
框架就是记录测试测哪些模块,以及测试结果。框架在运行时,会检测内部代码的返回值,一旦有false就失败,为true则进行下一条,都为true,则测试通过,如果是异步操作,需等待内置函数回调函数执行,或者超时。 所谓"断言",就是判断源码的实际执行结果与预期结果是否一致,如果不一致就抛出一个错误。
常用的测试框架: Jasmin、Qunit、Mocha
常用的断言库:assert、should、expect、chai
测试框架--Mocha
- describe():描述场景,在里面可以设定Context,可包括多个测试用例,也可以嵌套场景
- it():位于场景内,描述测试用例
- before():所有测试用例的统一前置动作
- after():所有测试用例的统一后置动作
- beforeEach():每个测试用例的前置动作
- afterEach():每个测试用例的后置动作
具体可以看另一篇文章 mocha + chai + supertest 测试 node server
自动化测试
puppeteer
Puppeteer是谷歌官方出品的一个通过DevTools协议控制headless Chrome的Node库。可以通过Puppeteer的提供的api直接控制Chrome模拟大部分用户操作来进行UI Test或者作为爬虫访问页面来收集数据。
优点:
所有API集成在一个node库中,环境配置简单 异步操作支持async await,事件可控制
const puppeteer = require('puppeteer');
(async () => {
// 打开浏览器对象
const browser = await puppeteer.launch({
// 有界面
headless: false,
// 窗口大小
defaultViewport: {
width: 1200,
height: 800,
},
// 模拟操作停顿时间,不然就一瞬间完成
slowMo: 120,
});
// 新建一个页面
const page = await browser.newPage();
// 页面内模拟用户操作
await page.goto('http://localhost:6789');
await page.waitForSelector('#main > div > div.sider.ant-layout-sider > div > ul > li:nth-child(2)');
await page.click('#main > div > div.sider.ant-layout-sider > div > ul > li:nth-child(2)');
await page.click('#main > div > div.content.ant-layout-content > div.content-body > div > div > form > div.ant-row-flex.ant-row-flex-center > button:nth-child(2)');
// 关闭浏览器
await browser.close();
})();
在开发中需要注意一下几点
- 关键位置最好加Id,便于拾取
- 每个新页面都是独立的,cookie等信息不共用
- 在模拟用户操作之前,最好确保selector已存在
- 可以先在有界面的状态测试,再切换headless模式
- 可以配合Mocha框架一起使用
自动化测试收益
自动化测试的收益 = 迭代次数 全手动执行成本 - 首次自动化成本 - 维护次数 维护成本
哪些地方要测试,需要根据具体项目决定
其他工具:
Selenium
Selenium是一个测试工具集,由Thoughtworks开发,分为两部分。Selenium IDE是一个Firefox浏览器的插件,可以录制用户行为,并快速测试。这个功能挺不错,感兴趣的可以玩一下。
而Selenium WebDriver是一个多语言的驱动浏览器的工具,支持Python、Java、Ruby、Perl、PHP或.Net。并且可以操作IE、Firefox、Safari和Chrome等主流浏览器。通过 open , type , click , waitForxxx 等指令来模拟用户行为,比如用Java测试:
public void testNew() throws Exception {
selenium.open("/");
selenium.type("q", "selenium rc");
selenium.click("btnG");
selenium.waitForPageToLoad("30000");
assertTrue(selenium.isTextPresent("Results * for selenium rc"));
}
PhantomJS
phantomjs利用的是webKit内核,全面支持web而不需浏览器支持,快速,原生支持各种Web标准。 速度快,使用简单。在puppeteer出来之前,大都使用phantom进行前端测试。 相对于puppeteer,其缺点也很明显:
- 非亲生,后来自我放弃,不再更新了
- 所有操作均需要异步调用,且无法确定操作成功的时机,往往要写很多的setTimeout。没有puppeteer中的async await舒服。
在puppeteer推出之后,就基本不用。
而casperjs是对PhantomJS的一层封装,api更加友好,不再介绍。