进入Jasmine的世界——超强Javascript测试框架

Jasmine是一个javascript的测试框架, 可以用来单元测试, 也可以进行其它类型的测试.

通常, 我们在项目比较大时会" 不得不引入测试", 原因很简单,因为这样在添加新的功能或者修复bug时不会引入新的bug,

这样会免去很多调试的时间,少做不少无用功。

谷歌的angularjs框架就是采用的测试驱动开发(TDD)方法,即先写需要的新功能的测试,然后再写代码。把写代码和调试代码的顺序

刚好反了过来。

言归正传,我们开始说Jasmine。

首先,我们在计算机上部署一个Jasmine,先让它跑起来

我们假定你正在使用ubuntu 14.04, 并且已经安装了git, 如果没有的话, 请运行

sudo apt-get install git

接下来,运行

cd ~
git clone https://github.com/pivotal/jasmine.git

这样会下载整个jasmine的工程,

然后我们再建立另外一个文件夹jasmine-test-prj来研究jasmine

mkdir ~/jasmine-test-prj
cd ~/jasmine-test-prj

接下来我们把jasmine源代码里dist文件夹下的jasmine-standalone-2.0.0.zip文件复制过来

mkdir jasmine
cp ~/jasmin/dist/jasmine-standalone-2.0.0.zip .
cd jasmine
unzip jasmine-standalone-2.0.0.zip

这样我们就得到了我们需要的文件,先来看看都有什么。

这个目录包含了jasmine的源代码和示例,那我们先运行一下示例。

google-chrome ~/jasmine-test-prj/jasmine/specrunner.html
 
  

这样我们发现打开的网页会自动运行一个已有示例。

下面我们就可以真正地开始使用jasmine这个框架了。

到目前为止,我们的目录结构应该是这样的, 如果不是的话, 请按照下面的图示建立一个相同的

jasmine-test-prj
--jasmine
然后我们建立一个js文件夹来存放我们的待测代码,再建立一个test文件夹来存放我们的测试代码
cd ~/jasmine-test-prj
mkdir js test
touch js/math.js test/test.html
然后在js文件夹下面先写一个简单的待测函数,在math.js文件中,内容如下
function add(a, b) {
  return a + b;
};
这只是一个hello world!式的文件, 后面我们会按照实际需求添加其它功能和测试

然后我们的test文件夹下面的test.html如下




  
  our huge math lib unit test

  
  

  
  
  

  

  






在上面的文件中, head部分大概包括了三个重要的块,一是jasmine本身需要的文件,二是待测文件,三是测试文件。

我们的math.js就在第二部分,而math-spec.js是我们用来测试math.js的文件。

下面我们来编写math-spec.js文件

touch ~/jasmine-test-prj/test/math-spec.js

该文件内容如下

describe('math', function() {
  it('should add', function() {
    expect(add(3, 5)).toBeCloseTo(8);
  });
});
然后,我们运行

google-chrome ~/jasmine-test-prj/test/test.html

congratulations! 我们成功了!

在上面那个测试文件中,我们可以按字面意思阅读,就是描述math,它应该做加法,希望把3和5加起来,结果等于8

在这里,toBeCloseTo用来做精确的数学式比较,例如比较1.0001和1.002是不是相等。

现在假设我们要写一个在项目中用到的数学库,它叫Math, 它应该定义一个类叫CoolMath类,然后有一个静态的公有方法叫add

现在我们用TDD的方式来开发,那么先来写测试,我们的math-spec.js经过修改,应该变成下面这样:

describe('our greatest and largest and complexed super math lib', function() {
  it('should has CoolMath class', function() {
    expect(CoolMath).toBeDefined();
  });
  it('should add', function() {
    expect(CoolMath.add(3, 5)).toBeCloseTo(8);
  });
});

然后我们刷新浏览器, 发现测试没有通过,当然,我们还没写代码呢!上面的测试的意思是要有个math类,并且已经定义,

使用了toBeDefined,如果我们希望某个变量没有定义,可以使用not.toBeDefined();很好理解吧?

那么, 我们对math.js做一些修改,如下

function CoolMath() {};
CoolMath.add = function(a, b) {
  return a + b;    
};

现在我们的数学库是一个函数对象,叫Math,然后它有一个公用的静态方法,就是加法。然后再刷新浏览器,通过了测试!

到这里,你会感觉每次刷新浏览器太麻烦了,那么建议你使用karma,我之前写过一篇介绍karma的文章。

再下来,我们又有了另外一种需求,我们想计算复数的加法,甚至我们想让我们的数学库支持自定义的加法,从而实现这个功能

同样我们先写测试:

describe('our greatest and largest and complexed super math lib', function() {
  it('should has CoolMath class', function() {
    expect(CoolMath).toBeDefined();
  });
  it('should add', function() {
    expect(CoolMath.add(3, 5)).toBeCloseTo(8);
  });
  it('should custom add', function() {
    expect(CoolMath.setCustomAdd).toBeDefined();
    function anAdd() {};
    CoolMath.setCustomAdd(anAdd);
    expect(CoolMath.addCustom).toEqual(anAdd);
  });
});

在上面的测试中, 我们希望数学库有一个添加自定义加法的接口,然后使用了这个接口,最后判断这个接口设置的函数是不是我们所需要的。

在这里使用了toEqual来比较对象是否相等,可以是两个object,然后计算每个属性是否都一样,从而给出结果。

我们math.js代码如下

function CoolMath() {};
CoolMath.add = function(a, b) {
  return a + b;    
};
CoolMath.setCustomAdd = function(cb) {
  this.addCustom = cb;
};

哈哈,通过了测试!这样以来,我们新加入了功能,确丝毫没有影响已有的功能,也没有什么bug.

接下来,我们想产生一个自然数的数列,同样先写测试

describe('our greatest and largest and complexed super math lib', function() {
  it('should has CoolMath class', function() {
    expect(CoolMath).toBeDefined();
  });
  it('should add', function() {
    expect(CoolMath.add(3, 5)).toBeCloseTo(8);
  });
  it('should custom add', function() {
    expect(CoolMath.setCustomAdd).toBeDefined();
    function anAdd() {};
    CoolMath.setCustomAdd(anAdd);
    expect(CoolMath.addCustom).toEqual(anAdd);
  });
  it('should generate a natural number sequence', function() {
    var ns = CoolMath.genNNS(1,3);
    expect(ns).toEqual([1,2,3]);
  });
});

这里,我们可以看到,toEqual是可以直接运用于数组的。我们希望产生1,2,3的数组,但是只写这个测试是不够的,如果我传入的参数是负数或者小数呢?

所以,我们要再加入一个测试来确保这个函数的健壮性:

describe('our greatest and largest and complexed super math lib', function() {
  it('should has CoolMath class', function() {
    expect(CoolMath).toBeDefined();
  });
  it('should add', function() {
    expect(CoolMath.add(3, 5)).toBeCloseTo(8);
  });
  it('should custom add', function() {
    expect(CoolMath.setCustomAdd).toBeDefined();
    function anAdd() {};
    CoolMath.setCustomAdd(anAdd);
    expect(CoolMath.addCustom).toEqual(anAdd);
  });
  it('should generate a natural number sequence', function() {
    var ns = CoolMath.genNNS(1,3);
    expect(ns).toEqual([1,2,3]);
  });
  it('should throw exception when arguments are illegal', function() {
    expect(function() {
      CoolMath.genNNS(-1,3);
    }).toThrow();  
  });
});

这里我们得到了一种测试函数抛出异常的方法,即建立一个匿名函数,然后在其中进行可以抛出异常的调用。

然后我们增加功能,完成错误检查:

function CoolMath() {};
CoolMath.add = function(a, b) {
  return a + b;    
};
CoolMath.setCustomAdd = function(cb) {
  this.addCustom = cb;
};
CoolMath.genNNS = function(s, e) {
  if(s < 0 || e < 0 || s > e){
    throw "need positive number!";
  }
  var arr = [];
  for(var i = s; i <=e; i++){
    arr.push(i);
  }
  return arr;
};

到目前为止,常用jasmine函数有toEqual, toBeDefined, toFalsy, toTruthy, 这些函数前可以加not, 例如not.toBeFalsy(), 意味希望得到一个true;

现在我们运行测试后,生成的测试结果是这样的:

  • our greatest and largest and complexed super math lib
    • should has CoolMath class
    • should add
    • should custom add
    • should generate a natural number sequence
    • should throw exception when arguments are illegal

我们的代码写到这里,会发现虽然只有5个测试,但是代码在逻辑上已经比较凌乱了,这是代码洁癖的人无法接受的。我们现在有2个测试来测试

和加法相关的, 还有两个测试来测数列,还有一个保证mathlib存在。在逻辑上这4个测试应该分成三部分,一部分是加法,一部分是数列,其它测试在另外一部分。所以,我们修改math-spec.js:

describe('our greatest and largest and complexed super math lib', function() {
  it('should has CoolMath class', function() {
    expect(CoolMath).toBeDefined();
  });
  describe("add", function() {
    it('should add', function() {
      expect(CoolMath.add(3, 5)).toBeCloseTo(8);
    });
    it('should custom add', function() {
      expect(CoolMath.setCustomAdd).toBeDefined();
      function anAdd() {};
      CoolMath.setCustomAdd(anAdd);
      expect(CoolMath.addCustom).toEqual(anAdd);
    });
  });
  describe('genNNS', function() {
    it('should generate a natural number sequence', function() {
      var ns = CoolMath.genNNS(1,3);
      expect(ns).toEqual([1,2,3]);
    });
    it('should throw exception when arguments are illegal', function() {
      expect(function() {
        CoolMath.genNNS(-1,3);
      }).toThrow();  
    });
  });
});

在上面的代码中,加法和数列的功能测试被放入了分开的块中,我们使用describe函数完成了测试代码逻辑上的分割,也证明describe是可以嵌套的。

此外,我们的测试结果也变了:

5 specs, 0 failures
  • our greatest and largest and complexed super math lib
    • should has CoolMath class
    • add
      • should add
      • should custom add
    • genNNS
      • should generate a natural number sequence
      • should throw exception when arguments are illegal

在这个测试结果中, 我们清晰地看到了两个功能块的测试结果,没有任何错误!


你可能感兴趣的:(进入Jasmine的世界——超强Javascript测试框架)