前言
作为服务端开发人员,Java的单元测试我们都了解,但是本文我们要来探讨关于JavaScript的单元测试。
你要问我为什么突然会写前端的博文,因为公司近期安排的调研任务 – angularjs ut,其实和angularjs spa相关开发,之前也做过一段时间,对此还是有些兴趣的,毕竟无论前端后端,技术思想基本是想通的。只是前端技术这两年发展的太迅猛,有点跟不上了。
在Karma的网站上只有karma+requirejs的介绍,而我们公司是requirejs+angularjs架构的,无耐网上关于karma+requirejs+angularjs的集成介绍文档少之又少,我在把这几个组件集成测试的时候,也调试了很久才跑起来的。。。都是泪啊,所以写下这一篇,方便跟我一样的后来者。
本文的思路,大致分为以下三个部分:
- 相关工具的介绍;
- 集成配置说明;
- 提供一个完整可运行的Demo;
- 配合jenkins持续集成;
好了,让我们开始吧~
名词
本文中,我们会讲到几个名词,对前端不了解的可以先Google下相关知识,先罗列一下:
- Karma
- Jasmine
- angularjs
- requirejs
- bower
- npm
- nodejs
前提假设,你已经了解requirejs和angularjs,并且机器上已安装nodejs、npm、bower。
首先,先了解下Jasmine
Jasmine
jasmine
英 [‘dʒæzmɪn; ‘dʒæs-] 美 [‘dʒæzmɪn]
茉莉
官方文档中对它的说明:
Jasmine is a behavior-driven development framework for testing JavaScript code. – Jasmine是一个面向JavaScript的行为驱动开发测试框架(BDD)。
它有点类似于java里的junit测试框架,不需要依赖任何的框架,将jasmine lib包导入到代码中,然后按照jasmine的语法进行测试用例编写,就可以测试任何的js代码。
下面给一个demo示例:
|
|
怎么样?是不是有似曾相识的感觉~没错,与java的junit语法逻辑差不多。
断言的语法:
|
|
类似于toBe
这样的matcher
函数,jasmine本身还有很多,这些函数通常情况下是够用了,如有不满足自己的比较需求的,jasmine还允许用户自定义,详细请见custom matchers,大家有兴趣可以研究下。
karma
karma
英 [‘kɑːmə; ‘kɜːmə] 美 [‘kɑrmə]
因果报应,因缘
它是一个js执行框架,可以用它在多个真实的浏览器上执行js代码,当然也包括js的单元测试(单元测试也是代码);
官方文档中对它的说明:
A simple tool that allows you to execute JavaScript code in multiple real browsers.
安装karma(它基于nodejs,所以安装之前请先安装nodejs、npm)
|
|
为了能在terimal中的任何位置都可以执行karma
命令,需要安装一个客户端karma-cli
,并把它配置到全局环境当中:
|
|
我们会使用jasmine作为测试框架,所以需要安装jasmine相关工具包
|
|
最后,因为karma的js代码需要在浏览器上执行,所以我们需要安装karma浏览器插件,在这里我们使用chrome浏览器;
|
|
karma支持的浏览器有这些:
|
|
安装大概就这些,karma主要是对配置文件的使用。下面我们开始把这些组件给用起来,在接下来的例子中,它将作为js单元测试框架jasmine的执行器来使用;
集成
这一部分主要说一下,如何将karma、jasmine、angularjs、requirejs给集成起来,让单元测试可以跑起来;
如果不想看下面的配置,我这里还提供了一个Demo,download之后,执行几条简单命令,就可以将单元测试跑起来,项目托管在GithubMonkeyIsSexy
https://github.com/monkeyissexy/karma-requirejs-angularjs
上,大家可自行查阅,看完觉得有帮助,可以动动手,给我加star哦~^_^
项目结构
先来看下最终的项目结构,好有个感觉
bower.json
项目用到的angularjs等lib包,是通过bower
工具来管理的,它是一个非常好用的,面向web的包管理工具。你自己写了个js类库,也可以将它发布到bower的官方类库下,这样别人就可以用啦!
Bower can manage components that contain HTML, CSS, JavaScript, fonts or even image files.
项目中如果有bower.json
,拿到项目之后,进入项目目录执行bower install
,就会下载中配置的相关包;不需要再将这些依赖包来回copy了。
本项目下的bower.json
|
|
如上,我们主要用到了三个依赖lib,对应的版本号也很清晰。
package.json
项目中用到的karma、jasmine相关的组件,我们使用npm
来管理。npm本身是作为nodejs的包管理器,而node又是由JavaScript编写的,所以现在越来越多的js类库也开始由npm来管理了,上面提到的bower.json中相关的三个依赖lib,也可以有npm来管理。
出于自己的习惯,将项目本身依赖lib由bower管理,比如:angular等;其它与项目本身无关的lib,由npm管理,比如kama等;这里萝卜青菜各有所爱,无论你用了其中的哪一个,都是可以的。尽量不要从网上自己下载lib,再copy到项目中,这样不利于管理。
如果项目中有package.json
文件,在拿到项目之后,进入项目目录执行npm install
即可;
本项目下的package.json
|
|
如上,我们依赖了一些karma、jasmine相关的工具包;
到此,我们把所有的依赖包都准备好了,下面开始配置项目:
项目结构图,方便理解:
|
|
main.js
作为一个requirejs项目,我们首先需要配置main.js
;
|
|
baseUrl: '/src'
,这个配置比较关键,它告诉requirejs,如果其它require module在define(['app'],function(app){...})
依赖时,从这个目录下开始查找,如app
,需要到src下查找app.js
。
app.js
下面,我们来看下app.js
,这里定义了angular module,与一些以来配置,比如:controller、filters等等;
|
|
这里将angularMocks
加载进来了,下面在写angular相关组件的test时会用到它(当然也可以在对应的测试类加载它),它类似于java里面的mockito
,mock一些业务逻辑依赖的服务,给你一个你所期望的结果;
好了,项目相关的配置就到这里,下面开始单元测试相关配置;
karma.config.js
这里就要开始讲到上面提到的karma工具了,上面我们使用npm install
将karma所需要的依赖安装了,为了我们可以在terminal的任何地方执行karma命令,还需要安装一个karma-cli
工具,安装方式如下:
|
|
这里的 -g
是指安装到全局目录中,因为默认是安装到当前目录的;
安装之后,在我们的项目目录下生成karma.conf.js
文件:
|
|
输入此命令敲回车之后,会出现一些询问,比如:
- 测试框架使用的哪个,默认是
jasmine
- 是否使用requirejs,左右键切换选择
y
- 加载的文件等等,这些可以不填写,直接敲回车使用默认项;
最后,会在项目目录下面生成一个叫karma.conf.js
的配置文件。
如下面所示,只不过多配置了一个测试覆盖率coverage
相关的配置:
|
|
coverage
是karma的一个组件,主要功能是在测试之后生成一个测试报告:包含测试覆盖率等,可细分到每一个js文件;
整体的karma.conf.js
如下:
|
|
说一下这里的files配置,一开始我想,所有的js通过app.js,让requirejs不都能加载进来吗?为什么还需要在这里加载呢,但是事实不是这样的。
test-main.js
其次,我们还需要配置一个test-main.js
,与main.js
相对应,来告诉karma在跑单元测试的时候,从哪来取测试文件、以及测试用到的依赖从哪里获取;
这一点也和java类似,一般单元测试也会有单独的config:
下面是test-main.js
的配置:
|
|
与main.js
相比,多了
|
|
这几个配置,需要时让requirejs来加载测试类的,好让karma来调用;
另外一个比较重要的点是
|
|
这个配置也是理解了很久,因为我们项目里面从来都没有/base
这个目录啊,不知道从哪里冒出来的。
原因是karma是通过启动webserver来加载上面配置的这些文件的,而webserver启动的context就是/base
对应到项目的目录上,一定要配置上。
测试举例
将这些配置完成之后,下面终于可以开始写测试类了,一路长途跋涉啊
userController.js
先拿controller举例,在src/controller目录下创建一个userController.js
|
|
这个controller的逻辑很简单,text 和 users,text赋值为hello字符串,users在UserFactory中通过发送http请求获取;
userFactory.js
src/factory/userFactory.js如下
|
|
这里使用了angular的$resource服务,它是$http的升级版,与restful风格api相吻合;
下面是我们的主角上场了,测试类:
userControllerSpec.js
位于test/controller/userControllerSpec.js
|
|
这个就是上文说的jasmine语法,只不过结合了requirejs而已;
测试类中的这几个语句,需要说明一下:
|
|
这里用到的是angularMocks,从上面app.js可以看出,已经依赖到myApp这个module中了,module
加载angular的module - myApp
进来,inject
注入一些angular的服务;
$httpBackend.when(...).respond(...);
这个写法是不是又跟mocktio很像呢,将某个http请求给mock了,这样当调用到这个http地址的时候,会按照期望的mock结果返回给调用者;
Demo项目中,还有关于filter、directive的测试,基本类似,不再敖述了,需要的话可下载代码下来研究。GithubMonkeyIsSexy
https://github.com/monkeyissexy/karma-requirejs-angularjs
执行测试
测试类写好了,现在可以来执行karma看效果了
在项目目录下执行如下命令
|
|
此时,会自动打开上面配置的chrome
浏览器来加载js代码并执行单元测试,执行完毕之后,在终端上可以看到测试结果概览;
|
|
如上展示,我们一共有4个case,并且都通过了,如果不通过,效果是这样的:
|
|
效果还是比较明显的。
同时,在test目录下面,会多出一个coverage文件夹,下面有一个index.html文件,这个是代码测试覆盖率的报告(不同于测试报告);这个是我们在karma.conf.js
中通过配置coverageReporter
来生成的;
这个报告长这样子
至此,整个集成配置就讲完了。。。
集成jenkins
实际项目使用中,我们可以结合jenkins一起来用;
集成jenkins的代码demo也已准备了一份,有需要可以自行下载:https://github.com/monkeyissexy/karma-requirejs-angularjs branch
jenkins-ci
目前,我们的jenkins部署在linux服务器上,但是karma的执行,需要打开浏览器,而linux服务器上没有,所以修改运行的浏览器,将js代码运行在PhantomJs
上面,它是一个不需要界面的浏览器,基于webkit;
jenkins上,我们可以安装各种plugin,下面我会演示如何把karma生成的测试报告集成到jenkins的测试报告插件中。
接下来,我主要已这个顺序来讲:
- 安装PhantomJS(幽灵)及karma phantomjs插件
- 配置karma运行于PlantomJS浏览器
- 安装jenkins插件
- 安装karma junit插件,并配置karma生成测试报告
- 配置jenkins获取karma测试报告
- 集成展示
安装PhantomJS相关支持
安装一个karma插件npm install karma-phantomjs-launcher --save-dev
(ps:安装这个插件之前,要先安装PhantomJS浏览器哦,我之前没安装这个,插件死活安装不上。。),然后将karma.conf.js中的browsers
设置为PhantomJs
。
安装jenkins插件
这里我用到的插件名称为Cobertura
安装截图我就不展示了,大致描述下,首页点击 Manage Jenkins –> Manage Plugins,然后点击available tab页签,在右上角的Filter框中输入Cobertura
,勾选结果点击右下角Install without restart
,等待进度条稍等片刻,完成安装;
安装karma junit插件并配置
下面,我们来安装测试报告生成工具,npm install karma-junit-reporter --save-dev
安装完毕之后,进入karma.conf.js进行配置:
|
|
如上,我们在reporters中添加了junit
,与之对应,还添加了一个junitReporter
配置。coverage
是我们上面演示的时候配置的,没有变化。
在jenkins上配置karma测试报告
下面这两个项在Post-build Actions
中配置,主要告诉jenkins在执行完build之后,去哪个位置获取build生产的测试报告、测试覆盖率的文件;
Publish Cobertura Coverage Report 这个是告诉jenkins从哪获取覆盖率的报告文件,我们demo项目生成的地址如下:**/test/coverage/**/*.xml
Publish JUnit test result report 这个是告诉jenkins从哪获取单元测试的报告文件,我们demo项目生成的地址如下:test/testReport/*.xml
测试&效果图
ps:在测试过程中发现,如果某一次build执行单元测试报错了,那么就只会生产单元测试的报告,不会生产覆盖率的报告了。
这个时候,我们就可以点击build来测试了,下面是效果图:
进入jenkins item首页,首先会有一个覆盖率和测试结果的概览趋势:
若单元测试出现错误,则如下:
若单元测试没有错误,则如下:
测试代码覆盖率,如下: