I code it

Code and Life

JavaScript的BDD框架Jasmine简介

Jasmin是一个使用JavaScript来完成BDD模式开发的小框架,今天看到一个同事用其来测试web上的js,于是不免技痒,就做了一些简单的测试。

下载Jasmin之后,解压之后,将其中的lib目录拷贝出来,这个就是所有你需要的东西了,然后将example下的SpecRunner.html拷贝出来,作为模版修改,即可完成自己的BDD了。比如我创建了一个目录,结构如下,其中测试用例文件(spec/rover-spec.js)和要测试的实现(src/rover.js),最终的目录结构如下图:

Image

rover实现了一个简单的探测器,一个探测器具有x,y坐标,并有一个朝向(faceTo)的属性,它可以转动方向,比如向左/向右转动,也可以移动,faceTo的方向移动一个单位。

   1: var Rover = function(x, y, faceTo){
   2:     this.x = x;
   3:     this.y = y;
   4:     this.faceTo = faceTo;
   5: }
   6:  
   7: Rover.dirMap = {
   8:     'North' : {
   9:         'Left' : 'West',
  10:         'Right' : 'East'
  11:     },
  12:     'South' : {
  13:         'Left' : 'East',
  14:         'Right' : 'West'
  15:     },
  16:     'East' : {
  17:         'Left' : 'North',
  18:         'Right' : 'South'
  19:     },
  20:     'West' : {
  21:         'Left' : 'South',
  22:         'Right' : 'North'
  23:     }
  24: }

测试的时候,预先设置rover的朝向为North,并处于0,0处,然后让其向右转动,预期中,此时的rover应该面向East(根据Rover.dirMap表中可知)。那么,测试用例应该是这样的:

   1: it('should face to East', function(){
   2:     robot.turn('Right');
   3:     expect(robot.getFaceTo()).toEqual('East');
   4: });


 

由于此时我们的rover还没有实现,robot的getFaceTo方法将返回‘NoDirect’:

   1: Rover.prototype.turn = function(dir){
   2:     return;
   3: }
   4:  
   5: Rover.prototype.getFaceTo = function(){
   6:     return 'NoDirect';
   7: }

 

Image(1)

然后我们就可以开始实现turn方法及getFaceTo方法,并不断测试,直到通过测试为止:

   1: Rover.prototype.turn = function(dir){
   2:     this.faceTo = Rover.dirMap[this.faceTo][dir];
   3: }
   4:  
   5: Rover.prototype.getFaceTo = function(){
   6:     return this.faceTo;
   7: }

 

Image(2)

我们还需要直到rover现在身在何处,所以加入getPosition方法:

   1: it('should in 1,1, and face to North', function(){
   2:     var pos = {'x' : 1, 'y' : 1};
   3:  
   4:     robot.turn('Right');
   5:     robot.move();
   6:     robot.turn('Left');
   7:     robot.move();
   8:  
   9:     expect(robot.getFaceTo()).toEqual('North');
  10:     expect(robot.getPosition()).toEqual(pos);
  11: });

 

并重复上述的过程,直至两个测试都通过:

Image(3)

Jasmine还提供一些其他的方法,比如在所有测试用例启用之前的beforeEach方法,支持自定义matcher等。 比如,需要在最开始的时候,实例化一个rover,然后在各个用例中共享这个rover,即可将此部分代码写在beforeEach中:

   1: var robot;
   2:  
   3: beforeEach(function(){
   4:     robot = new Rover(0, 0, 'North');  
   5: });

 

或者自定义一个matcher,可以使得判断语句更加的易读,事实上,大量使用matcher后的代码看起来很像是专门为测试而构建的DSL,对于开发团队中各个角色间的交流提供了很大的帮助:

   1: beforeEach(function(){
   2:     this.addMatchers({
   3:         toBeFaceToEast: function(excepted){
   4:             var rover = this.actual;
   5:             return rover.getFaceTo() == 'East';
   6:         },
   7:         toBeFaceToWest : function(excepted){
   8:             var rover = this.actual;
   9:             return rover.getFaceTo() == 'West';
  10:         }
  11:     });
  12: });

 

比如,上边的代码中,我们定义了toBeFaceToEast 的matcher,这样在测试用例中的代码就可以变为:

   1: it('should face to East', function(){
   2:     robot.turn('Right');
   3:     expect(robot).toBeFaceToEast(robot);
   4: });

 

当然,这个过程对BDDTDD都仅仅做了很简单的演示,不过可以看出,这种开发方式确实很有意思,而且一方面可以使得开发和QA分离,另一方面使得开发人员更关注业务需要,而不是根据自己的想法实现很多不必要的功能出来(这个应该是开发人员经常会犯的错误),从而”提高”开发效率。

Comments