11月阅读周·编写可测试的JavaScript代码:集成测试篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读十个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》、《JavaScript异步编程设计快速响应的网络应用》。
当前阅读周书籍:《编写可测试的JavaScript代码》。
集成测试
集成测试重要性
所有类型的测试都依赖于整个应用程序的启动和运行。无论是在测试环境中还是在生产环境中,所有的应用程序都必须组合在一起。可测试的JavaScript使用了最小依赖的小型代码块;所有这些代码都组合在一起时,能够实现各取所需。基于事件的架构是大量使用的松耦合,并且在编译后能够协同工作的一个例子。因此,必须要对部署和启动系统进行自动化。系统启动之后,才能进行测试。
对Web应用程序进行集成测试需要在浏览器中运行应用程序,并确保其功能按预期运行。通过单元测试隔离每个测试部分是一个很好的开始,但必须要遵循集成测试。集成测试用于测试在更大范围内运行的代码组合。在该级别上,不会对依赖项进行模拟(mock或stub),我们是在应用程序级别进行测试。
Selenium
在浏览器中对JavaScript进行测试通常涉及Selenium。Selenium测试通常需要在浏览器的同一个沙盒上运行大量的Java代码以便运行测试,以及一个用于控制远程浏览器的客户端API。Selenium2 / WebDriver可以控制Firefox、Chrome,以及Mac OS X版和Windows版的IE浏览器。
可以使用各种语言编写Selenium测试,也可以通过键盘和鼠标操作使用Firefox插件生成各种语言的测试。Selenium还提供了一组断言和验证函数用于测试当前页面,以确保当前当前状态是有效的。
使用Selenium IDE编写测试是最快捷的方式。对于任意版本的Firefox来说,可以在SeleniumHQ网站下载最新版本的IDE(本文撰写时,其版本是1.10.0),并让Firefox安装该插件。
加载网站并打开Selenium IDE(工具→Selenium IDE)。将Base URL设置为当前Web应用程序所在页面的URL。点击Selenium IDE右上角的记录(record)按钮,一旦在程序上进行点击或者输入操作的时候,Selenium就开始跟踪鼠标和键盘操作了。再次点击记录(record)按钮即可停止记录。
选择File→Export Test Case As,可以将点击和输入到保存不同语言版本的Selenium2 / WebDriver测试,或者是原始Selenium(Remote Control)工具的测试,不过,如果是Selenium新手,则不应该使用该原始工具。
通过点击绿色的播放(play)按钮,可以在Selenium IDE中重新运行这些测试。Selenium IDE窗口底部的日志会让我们了解运行过程。
使用Selenium的一个常见问题是,它默认使用元素id来识别互动的元素,使用JavaScript框架动态生成元素id时,就会导致测试失败,因为这些动态元素id在后续运行期间不会被发现。幸运的是,可以在Selenium IDE的Target文本字段里使用XPath或CSS表达式而不是使用默认的ID方式来定位元素(当然,如果设置了元素的ID也不会有问题),从而解决了这一问题。改变选择器时,点击Target文本字段旁边的查找(find)按钮即可查找想定位的元素。
使用JUnit,也可以通过命令行运行所保存的测试用例。将测试用例导出为JUnit 4(WebDriver后端)文件并对其进行命名。IDE将会在文件的顶部生成如下声明:
package com.example.tests;
修改上述声明以匹配环境,或者删除了之。
此时,需要当前版本的Selenium服务器和客户端驱动程序。从SeleniumHQ站点下载当前版本的Selenium服务器(本文撰写时是版本2.28)以及Java Selenium客户端驱动程序(本文撰写时是版本2.28.0)。需要对Java Selenium客户端进行解压。
要编译导出的Selenium脚本,需要使用selenium-server JAR:
java -cp path/to/selenium-server-standalone-2.28.0.jar test.java
上述命令将会编译导出的Selenium测试用例。启动Selenium服务器才能执行测试。
现在就可以运行JUnit测试用例了(如下命令都在一行上):
java -cp path/to/selenium-server-standalone-2.28.0.jar:downleads/
selenium-2.20.0/libs/junit-dep-4.10.jar:.
org.junit.runner.junitcore test
该命令需要提供Selenium服务器JAR和JUnit JAR(如果没有的话,可以从Java Selenium客户端代码中找到)的路径。
使用Selenium IDE是很笨重的,最好是自己手工编写测试用例(可以使用JUnit)。但请注意,这就需要学习HTML中的XPath或CSS选择器,或者使用ID属性,以便Selenium可以“抓住”这些元素并操作应用程序:点击链接和按钮、拖拽、编辑表单元素,等等。
可以使用Selenium函数中的assert或verify家族函数来验证应用程序的功能。注意,assert家族函数验证失败时会直接跳到下一个测试函数,而verify家族函数验证失败时会继续运行函数内部后续的代码。在Selenium测试中,应该总是使用assert家族函数。
grid
Selenium还支持“网格(grid)”配置,其包括一个中心“hub”以及很多分布式的“分支(spokes)”,这些分支实际用于产生浏览器和反馈命令。这对于Selenium任务的并行处理是有利的,或其可以作为Selenium运行器的一个重要存储库,让开发人员和QA人员访问,而不需要每个人都运行和维护自己的Selenium实例。
每个分支使用其产生的浏览器连接到中央hub,当匹配的功能列表传入时,该hub处理该分支的Selenium任务。一个单个的hub可以服务于Mac、Windows和Linux客户端所运行的不同浏览器。
很方便,最新版本的Selenium独立服务器同时支持WebDriver和Remote Control的网格。启动一个网格中心(grid hub),简单地使用如下命令即可:
java-jar selenium-server-standalone-2.28.0.jar -role hub
一旦hub启动了,接着使用如下代码(所有代码都在一行上)启动链接到该hub的节点,并产生浏览器——这些节点可以作为hub在同一个主机上运行,也可以在远程主机上运行:
java-jar selenium-server-standalone-2.28.0.jar
-role node-hub http://localhost:4444/grid/register
该设置最优秀的地方就是客户端代码不需要修改!使用webdriverjs,可以利用额外的节点进行并行化处理;而独立服务器一次可以处理一个请求,每个节点可以同时处理多个请求。将浏览器指向 (在hub运行的主机上),该页面将直观地提供所有链接到hub上的节点数,以及并行处理过程中每个节点负责处理的任务数。在老版本Selenium1 Remote Control的网格中,每个节点只能处理一个Selenium任务。而新版基于WebDriver的网格中,则可以处理多个任务。默认是5个,但可以在每个节点上使用-maxSession <num>命令行修改这一数字。
现在可以使用节点和任务重新创建Selenium hub以支持所有的测试了,不限定编写测试的语言,也不限定于要测试的浏览器。
CasperJS
Selenium不是唯一的浏览器集成测试框架。CasperJS构建在PhantomJS之上,其提供了与Selenium类似的功能,但完全不限制环境。使用纯JavaScript或CoffeeScript,可以编写与Web应用程序交互的脚本,并测试交互结果,包括截图,不使用任何Java。使用带有最新版本PhantomJS(撰写本文时是版本1.7.0)的CasperJS时,不再需要运行X11或Xvfb以启动PhantomJS WebKit浏览器了,因为现在的PhantomJS是构建在Lighthouse Qt 4.8.0之上,是一个与设备无关的显示框架。这意味着,可以在服务器上进行真正的无关于运行环境的集成测试了。
要使用CasperJS,首先必须从code.google phantomjs网站安装最新版本的PhantomJS。下载对应操作系统的二进制版本是最简单的,但是从源代码进行构建也不是很难。
CasperJS和Selenium之间最大的区别是需要指定要进行截图的屏幕的确切大小;CasperJS不能捕获整个浏览器区域,而Selenium可以。
CasperJS还有其他一些使用技巧,包括自动将测试结果导出成JUnit XML格式的文件。如下是完整的脚本:
var casper = require('casper').create();
casper.start('http://search.yahoo.com/', function () {
this.fill('form#sf', { p: 'javascript' }, false);
this.click('#yschbt');
});
casper.then(function () {
this.capture('results.png', {
top: 0,
left: 0,
width: 1024,
height: 768,
});
this.test.assertexists('#resultcount', 'got result count');
});
casper.run(function () {
this.test.renderresults(true, o, 'test-results.xml');
});
除了将测试结果输出到控制台,test-results.xml文件将包含JUnit XML格式的测试输出,该格式很容易被Hudson/Jenkins之类的构建工具所理解。如下是运行该测试之后的测试结果:
<testsuite>
<testcase classname="ss" name="Got result count">
</testcase>
</testsuite>
当然,还要在Internet Explorer中测试代码,(很不幸)我们的大部分用户很可能都在使用它,这种情况我们必须要使用Selenium。但对于无环境依赖的快速测试来说,CasperJS是一个很好的辅助。
总结
除了单元测试以外,对应用程序进行集成测试、性能测试和负载测试同样重要。编写集成测试,并在“真实”的浏览器上或自动构建环境中运行非常简单。大部分事情确实是很简单,一旦样板代码和配置准备好了,就能够很容易地添加测试。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)