发布于 2016-01-08 10:13:58 | 181 次阅读 | 评论: 0 | 来源: 分享
Karma javascript测试工具
Karma 是一个简单的javascript测试工具,它允许在多个真正的浏览器执行JavaScript代码.
这篇文章主要来自 karma 作者的一篇论文,主要是说 karma 的由来,通过这篇文章,可以了解下 karma 的设计思想,这样大家在做前端单元测试时,也能了然于心。
JavaScript
作为 web
端使用最广泛的编程语言,它是动态语言,缺乏静态类型检查,所以在代码编译期间,很难发现像变量名写错
,调用不存在的方法
等错误,除非在运行时才能暴露出来,所以非常有必要有一个测试工具来验证你的代码。
karma
就是在这样的背景下产生的, 它是一个 runner
, 旨在帮助开发者简单而又快速的进行自动化单元测试, 目前已经用在很多大型的项目, google
和 youtube
这些公司都在用它, 在 npm
官方网站上, 它也是一个比较流行的 npm
模块。
下面会讲述 karma
的由来, 以及现有解决方案的比较, 最后会谈及 karma
的设计与实现。
下面从两方面来说明
DOM
操作在不同浏览器上的表现TDD
被证明是有效的软件编写原则,它能覆盖更多的功能接口快速反馈
你的功能输出,验证你的想法代码重构的安全性
,没有一成不变的代码
,测试用例能给你多变的代码结构一个定心丸。代码 API 使用文档
,而且还是定时更新的,对 API
的使用者有很大的帮助视图
与功能
分离,这样的话,你的代码也便于维护和理解。相信现在前端开发者对测试都不是很依赖,因为缺少好用的工具,甚至都不会去写测试。
其它语言像 Java, Python ,C#
, 测试工具基本都会集成到 IDE 中,基本都能马上看到测试结果,而且像 C++
这种编译性语言,在编译
期间基本都会找到代码里的错误
当我们把焦点放在前端开发中的 JavaScript
上来时,上面语言的一些优点完全没有了,关键就是缺少一个流程控制的测试工具。
Selenium
是一个完整的测试工具,它是一个比较成熟的古老测试工具之一,它比较适合用来做高级测试,比如 e2e
测试。
它的核心思想就是代理注入
,Selenium
通过代理打开浏览器 URL, 有一个后台的 server
在监听,当有请求时,会往浏览器中注入脚本,之后就通过这个脚本来跟 server
端通讯。
开发者可以使用 Selenium
客户端 API 来编写单测, 比如跳转 URL
或者单击按钮
Selenium
客户端有多种实现,比如 Java
,Python
,Ruby
,开发者可以使用自己熟悉的语言来编写单例
WebDriver
是基于 HTTP 协议来跟浏览器通讯的,Selenium 2
实现了完整的 WebDriver
协议
WebDriver
可以调用更底层的 DOM
API, 像 Safari
不支持这种协议的话,就采用 Selenium 1
那种代理注入
的方式通讯
Mocha
既是测试框架,也是一个测试 runner ,它主要用在 Node.js
里的单元测试,当然也可以用在浏览器端,不过得手动配置各种适配脚本。
JsTestDriver
是直接在浏览器里执行的,所以可以直接调用 DOM
和浏览器端 API, 不过先得开启一个服务端程序, 它的核心是一个 runner 程序,当运行单测时,runner 会通知服务端来更新浏览器端,比如重新加载 js
脚本,返回测试结果到服务端。
大部分的测试框架,像 Jasmine
和 QUnit
都包含一个 HTML
runner, 开发者要自己维护加载的前端资源, 手动刷新页面, 测试结果以 HTML 形式显示
下面主要比较下,上面这些工具框架的优缺点
Selenium
和 WebDriver
不支持直接访问 JavaScript 代码能力,只是在浏览器里执行,通过各种命令来操作,它的最大优点就是可以访问原生 DOM 事件,这个是 JavaScript 里是很难模拟的,所以这非常适合做 e2e
测试,WebDriver
基本是 e2e
测试的标准了。HTML runners
仅仅是一个测试框架,可以在浏览器端执行然后输出结果,它缺少一种流程管理机制,而且跟第三方编辑器以及 CI Servers 都不能进行通讯Mocha
提供了一种很好的测试控制,但是它比较适合用在 Node.js
环境里,浏览端运行的话,也会产生 HTML runners
类似的问题JsTestDriver
是最接近完美的测试工具了,它可以直接访问浏览器端 API , 而且也有管理工具,可以在命令行里进行控制,但是它有两个主要的问题
Karma
就是对 JsTestDriver
的改进,提供了文件监听以及预处理能力, 文件监听
很重要,它方便与各种编辑器接入, 减少用户运行单测的成本,直接保存文件就可以了, 下面是一张各种工具的对比图karma
设计目标主要有下面四点:
karma
是一个典型的 C/S
程序,包含 client 和 server ,通讯方式基于 Http
,通常情况下,客户端和服务端基本都运行在开发者本地机器上。
一个服务端实例对应一个项目,假如想同时运行多个项目,得同时开启多个服务端实例。
Server
是框架的主要组成部分之一,它内部保存了所有的程序运行状态,比如 client 连接,当前运行的单测文件,根据这些数据状态,它提供了下面几个功能, 下图是 server 的结构
注意:连接 server 的 client 浏览器有多种类型,比如 PC,iphone,TV 端等
Server
端运行在开发者机器上,根据测试配置文件,它能快速的访问本地测试文件
下面主要说下 Server
端的 4 个组成部分:
Manager
的主要责任就是跟 client 进行通讯,比如广播信号通知 client 开始测试以及收集 client 返回的测试结果
Manager
内部维护了两个数据模型:
作为通讯网关,它会利用这两种数据模型去通知服务端其它组成部分,比如测试完成之后,通知输出结果展示。
基于 connect
的一个 server , 主要是提供访问本地静态资源用的,这里的资源包含:JS 测试框架,断言库,测试用例以及它的依赖等。
利用上面的测试数据模型,reporter 展示输出结果,输出端包含本地命令行,文件或者一个 ci server。
watcher 主要是监听本地文件改变,内部维护了一个数据模型,包含所有测试相关的文件,它能保证 Web Server
拉取的静态资源都是最新的,同时也能保证文件访问成本以及网络成本,永远只加载修改的文件。
Client
是测试文件真正运行的地方,比如一个 PC,iphone,tablet 端的浏览器,通常情况下跟 server
是同一个物理机,当然也可以运行在不同的机器,通过 HTTP
来通讯
多个 Client
可以与一个 Server
进行通讯
这里主要是跟 server
进行消息通讯,以及与其它 client
组成部分进行交互,比如测试框架 mocha
测试框架不是系统的一部分,karma
灵活支持第三方测试框架,以插件的形式接入。
这里包含用户所有的测试相关文件,它是通过 web-server
模块来获取,测试文件由 test framework
来执行。
主要描述 server
与 client
的通讯时机
Karma 是用 JavaScript 实现的, server 端运行在 Node.js 环境下, client 运行在浏览器环境下, 为什么选择使用 Node.js
,下面是作者的几点看法:
上面啰嗦了这么多,终于要说到正题了,下面主要说下 server 和 client 的核心实现
这部分主要说下 server 的核心实现,下图中实线代表直接方法调用,虚线代表通过事件通讯
如图 server 的组成部分
文件监听是基于 chokidar
npm 模块来实现的,它是对 Node.js 底层 API 的封装(fs.watch 和 fs.watchFile), 系统启动时,chokidar
根据配置文件会对 glob
形式的文件字符串进行监听
chokidar
屏蔽了不同系统之间的差异问题,而且提供了一些 Node.js 底层 API 不支持的功能,比如对整个文件目录进行监听
文件模型主要是为了提高访问本地文件以及网络文件的性能, 它包含跟测试相关文件的原数据描述,这些文件是通过 karma.conf.js
配置文件里的 files
字段, glob
形式的文件字符串, 比如 js/src/*.js
, test/**/*.js
,来绑定监听的。
当你添加
,删除
,修改
文件,都会触发文件模型里定义的 file_list_modified
事件,该事件有两个观察者
web server
, 接收到最新的文件数据列表manager
, 刷新浏览器,重新执行测试文件内部主要有两个类,FileList
,File
, 前者包含多个 File
实例, File
单个实例会保存测试文件的一些基本数据,比如路径等。
如下图描述
这块是基于 connect
来实现的,然后系统提供不同的 handlers
来解决不同的请求,主要有下面四种 handle :
当 web server
请种各种文件时,需要跟上面的 fs model
进行交互,拿到具体的文件原数据之后,做最合适的请求响应
reporter 会接收来自 client 端的请求,然后展示相对应的单测成功或者失败消息,这块 karma
支持以插件的形式扩展自己想要的 reporter 效果。
client 是单测最终运行的地方,类似一个 web app , 跟 server 端通讯利用 socket.io
, 执行单测在一个独立的 iframe
中。下面是它的结构图
client manager 是对socket.io
调用的一个简单的包装,它提供了一些 API , 给 test framework adapter 来调用, 方便与 server 端通讯, 下面是它的一些 API 列表
manager 运行在 HTML 里的主窗口内,它提供的 API 会暴露给 iframe 里的 test framework adapter 来调用
client 包含一个独立的 iframe , 它会加载框架自带的 context.html
文件,头部会引用一堆以 script
标签的资源文件,这些文件来源于你的配置文件,通过 web server
里的 handle
来构建完整的 context.html
文件内容。
用独立的 iframe
来执行单测有很多好处,不会污染全局的 window 对象, 方便文件更新后, 重新执行单测
这是第三方测试框架对应的 karma
适配器,本身不是框架的一部分,是以插件的形式加载到 HTML 里的, 这样就可以保证, 无论什么测试框架,karma
都可以无缝接入, 想要系统适应不同框架的问题,就得提供一套约束出来,想要开发一个适配器,至少得实现 __karma__.start
方法, 这个方法最终会被 manager
来调来, 它是执行本地单测的入口方法,下面是 Jasmine
测试框架的适配器代码
client 和 server 端通讯采用 socket.io
karma
框架是基于 di
开发的,di
这个 npm 模块也是 karma
作者自己开发的,是一个依赖注入库,这里主要应用了设计模式里的 ioc
原则, 简单的说, 就是函数的参数不是手动实例,而是通过依赖注入进去的,这样可以极大提高系统的扩展性以及灵活性, 而且代码本身更容易测试了,推荐大家在开发复杂系统时,使用这种设计思想。