版展开认证,环境足以接纳 Jasmine 做单元测试吗

【转】动用 贾斯敏 举行测试驱动的 JavaScript 开发

      
近年来的种类在利用AngulaJs,对JS代码的测试难题就摆在了眼下。通过对照大家拔取了
Karma  + jasmine ,使用 Jasmine做单元测试 ,Karma
自动化达成,当然了一旦采纳 Karma  + jasmine 前提是必须安装 Nodejs。

写在前边

本文种介绍部分jasmine的基本概念和语法,并付出不难易懂的以身作则。适合初学jasmine者,即使您曾经触发并动用过jasmine,或然并不太符合你

(原文:http://www.cnblogs.com/haogj/p/4778808.html)

图片 1

Jasmine 为 JavaScript 提供了 TDD
(测试驱动开发)的框架,对于前端软件开发提供了完美的品质担保,那里对
Jasmine 的布置和运用做二个证实。

脚下,Jasmine 的流行版本是 2.3 版,那里以 2.3
版展开认证。网上一度有一部分有关 Jasmine的素材,然而,某些资料相比较遥远,已经与现有版本不相同等。所以,那里尤其以新型版举办求证。

设置好 Nodejs ,使用 npm
安装好要求的包,写了三个测试用例,测试通过,很好很有力。 没有 Nodejs
环境足以行使 Jasmine 做单元测试吗?当然可以,我们可以到
官网下三个演示看一看,相比较简单。今日先讲一下如若直白采取

Jasmine 前端单元测试框架

Jasmine是面向行为使得开发(BDD)的Javascript单元测试框架。它不借助于于其他任何javascript框架,语法清晰不难,很简单上手写出测试代码

  • BDD 行为驱动开发,是一种新的飞速开发方法。相对于TDD(测试驱动开发),它更趋向于须求,要求一块利益者的到场,强调用户故事和表现;是面向开发者、QA、非技术人士或商业插足者共同参与和清楚的支出活动,而不是TDD不难地只关切开发者的方法论;
  • TDD测试驱动开发,是一种差异于传统软件开发流程:开发为止再测试出席的时尚开发方法。必要把品种按效能点划分,在编制各个功用点前先编制测试代码,然后再编辑使测试通过的效应代码,通过测试来促进全体开发工作。

借使您想深切精晓BDD和TDD:
可观望下关于前端开发谈谈单元测试那篇小说,其余整理了骨干书籍,推荐给大家:

  • 测试驱动开发byExample
  • 测试驱动开发的措施

1. 下载

官网地址:http://jasmine.github.io/

官网文档地址:http://jasmine.github.io/2.3/introduction.html

下载地址:https://github.com/jasmine/jasmine/releases

在 GitHub
上提供了独立版本 jasmine-standalone-2.3.4.zip 和源码版本,如果利用的话,直接运用
standalone 版本即可。

解压之后,可以博得如下所示的文件结构。

图片 2

中间,lib 中是 Jasmine 的贯彻公文,在 lib/jasmine-2.3.4
文件夹中,可以看出如下的文件。

图片 3

开拓最外层的 SpecRunner.html ,那是1个 Jasmine的模版,其中提供了测试的示范,大家得以在行使中平昔套用这么些模板。其中的故事情节为:

图片 4

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner v2.3.4</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.3.4/jasmine_favicon.png">
  <link rel="stylesheet" href="lib/jasmine-2.3.4/jasmine.css">

  <script src="lib/jasmine-2.3.4/jasmine.js"></script>
  <script src="lib/jasmine-2.3.4/jasmine-html.js"></script>
  <script src="lib/jasmine-2.3.4/boot.js"></script>

  <!-- include source files here... -->
  <script src="src/Player.js"></script>
  <script src="src/Song.js"></script>

  <!-- include spec files here... -->
  <script src="spec/SpecHelper.js"></script>
  <script src="spec/PlayerSpec.js"></script>

</head>

<body>
</body>
</html>

图片 5

可以看看里边引用了 lib/jasmine-2.3.4/jasmine.js, lib/jasmine-2.3.4/jasmine-html.js
和 lib/jasmine-2.3.4/boot.js 三个系统文件,其中 boot.js
是网页情况下的启航文件,在 张丹 的 jasmine行为使得,测试先行 那篇小说中,要写一个report.js 的开行脚本,那里曾经毫无了,直接运用 boot.js 就足以。

页面上边引用的  src/Player.js 和 src/Song.js
是我们的测试目的,而 spec/SpecHelper.js 和 spec/PlayerSpec.js
则是八个照应的测试文件,测试用例就定义在 spec 中。

jasmine 做单元测试

学习jasmine

2. 测试的定义

咱俩依旧直接看示例,

二个是 Song.js,那里定义了壹个 Song
的类,通过原型定义了一个persistFavoriteStatus
实例方法,注意,这里还未曾兑现,若是调用则会抛出极度。脚本如下。

图片 6

function Song() {
}

Song.prototype.persistFavoriteStatus = function(value) {
  // something complicated
  throw new Error("not yet implemented");
};

图片 7

除此以外二个是 player.js,定义了 Player 类,定义了贰个歌星,通过原型定义了
play, pause, resume 和 makeFavorite 实例方法。对象有壹个 isPlaying
的情景,其中 resume 还没有完成。

图片 8

function Player() {
}
Player.prototype.play = function(song) {
  this.currentlyPlayingSong = song;
  this.isPlaying = true;
};

Player.prototype.pause = function() {
  this.isPlaying = false;
};

Player.prototype.resume = function() {
  if (this.isPlaying) {
    throw new Error("song is already playing");
  }

  this.isPlaying = true;
};

Player.prototype.makeFavorite = function() {
  this.currentlyPlayingSong.persistFavoriteStatus(true);
};

图片 9

上边看测试的概念,具体测试的表明,直接加在注释中。

图片 10

describe("Player", function() {
  var player;
  var song;

  beforeEach(function() {
    player = new Player();
    song = new Song();
  });

  // 检测正在歌手进行的歌曲确实是指定的歌曲
  it("should be able to play a Song", function() {
    player.play(song);
    expect(player.currentlyPlayingSong).toEqual(song);

    //demonstrates use of custom matcher
    expect(player).toBePlaying(song);
  });

  // 进行测试的分组,这里测试暂停状态
  describe("when song has been paused", function() {
    beforeEach(function() {
      player.play(song);
      player.pause();
    });

    // isPlaying 的状态检测
    it("should indicate that the song is currently paused", function() {
      expect(player.isPlaying).toBeFalsy();

      // demonstrates use of 'not' with a custom matcher
      // 
      expect(player).not.toBePlaying(song);
    });

    // 恢复
    it("should be possible to resume", function() {
      player.resume();
      expect(player.isPlaying).toBeTruthy();
      expect(player.currentlyPlayingSong).toEqual(song);
    });
  });

  // demonstrates use of spies to intercept and test method calls
  // 使用 spyOn 为对象创建一个 mock 函数
  it("tells the current song if the user has made it a favorite", function() {
    spyOn(song, 'persistFavoriteStatus');

    player.play(song);
    player.makeFavorite();

    expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
  });

  //demonstrates use of expected exceptions
  // 异常检测
  describe("#resume", function() {
    it("should throw an exception if song is already playing", function() {
      player.play(song);

      expect(function() {
        player.resume();
      }).toThrowError("song is already playing");
    });
  });
});

图片 11

动用浏览器直接打开 SpenRunner.html 看到的结果

图片 12

可以看到测试都通过了。

一旦我们将首先个测试 expect(player.currentlyPlayingSong).toEqual(song);
改成 expect(player.currentlyPlayingSong).toEqual( 1 );

测试通不过,展现会变成那样。

图片 13

   回顾示例

jasmine学习环境搭建

在始发攻读jasmine在此之前,搭建贰个测试jasmine语法的求学环境是很有要求的,但其实支出中不引进应用那样的条件

 3. 语法

   jasmine 示例下载地址
 https://github.com/jasmine/jasmine/releases
选拔新型版本下载下来示例代码结构如图

获取安装包

可以在开源社区github上下载
jasmine-standalone-2.4.1.zip;

3.1 describe 和 it

describe
用来对测试用例举办分组,分组可以嵌套,每种分组可以有三个讲述表达,那个注解将会师世在测试结果的页面中。

describe("Player", function() {
      describe("when song has been paused", function() {

而 it
就是测试用例,每种测试用例有2个字符串的表达,匿名函数内就是测试内容。

  // 检测正在歌手进行的歌曲确实是指定的歌曲
  it("should be able to play a Song", function() {
    player.play(song);
    expect(player.currentlyPlayingSong).toEqual(song);
  });

测试结果的断言使用 expect 举办,函数内提供测试的值,toXXX
中则是期望的值。

地方的测试使用 toEqual 举行相等断言判断。

        图片 14

布局安装

下载后解压.zip压缩包,拿到如下的目录结构:

图片 15

jasmine安装包解压后目录结构

  • lib目录下富含的jasmine的源代码,把她们(jasmine.css,jasmine.js,jasmine-html.js)引入页面中,就协会了多少个jasmine的运转器(Runner);
  • 开发基于jasmine的测试用例并再引入到jasmine的运维器(页面)中,就从头测试工作了。

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.4.1/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="lib/jasmine-2.4.1/jasmine.css">
  <script type="text/javascript" src="lib/jasmine-2.4.1/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.4.1/jasmine-html.js"></script>

sepc,src
和SpecRunner.html是jasmine的1个官方完整example,使用浏览器直接打开SpecRunner.html就足以看来example的测试结果

3.2 beforeEach 和 afterEach

示范中还现出了 beforeEach。

图片 16

  var player;
  var song;

  beforeEach(function() {
    player = new Player();
    song = new Song();
  });

图片 17

顾名思义,它象征在本组的各种测试从前需求进行的准备工作。在大家那边的测试中,总要用到
player 和 song 那七个对象实例,使用 forEach
保险在各种测试用例执行此前,重新对那多个目的开展了起首化。

afterEach 会在每2个测试用例执行之后执行。

  lib 文件夹上边:  boot.js 运转文件  ,

jasmine语法介绍

阅读上例SpecRunner,你应有有了有关jasmine测试用例怎么着去写的题材,带着题材来上学下jasmine的语法是再好不过的章程了

3.3 自定义的预感

除却系统定义的 toEqual
等等断言之外,也得以使用自定义的预见,在地点的以身作则中就出现了 toBePlaying
断言。

    //demonstrates use of custom matcher
    expect(player).toBePlaying(song);

这些自定义的断言定义在 SpecHelper.js 文件中。

图片 18

beforeEach(function () {
  jasmine.addMatchers({
    toBePlaying: function () {
      return {
        compare: function (actual, expected) {
          var player = actual;

          return {
            pass: player.currentlyPlayingSong === expected && player.isPlaying
          };
        }
      };
    }
  });
});

图片 19

其中调用了 jasmine 的 addMatchers 函数进行定义,原来那里不叫断言,称为
matcher ,相当于匹配器。

预见是二个函数,重返2个对象,其中有二个 compare
的函数,这几个函数接收多个参数,第1个是实际值,第二个为希望的值。具体的预感逻辑自身定义,那里相比歌唱家演唱的目的是还是不是为大家传递的靶子,并且明星的情形为正值表演中。

断言函数要求回到二个目标,对象的 pass 属性为2个 boolean
值,表示是不是经过。

            console.js 输出资助文件,

describe方法

describe是jasmine用于描述测试集(Test
Suite)的大局函数,常常有八个参数,一个字符串String,2个方法function;字符串用来讲述Test
suite,function里的代码就是测试代码,表示2个测试集合;
三个测试集合能够涵盖三个spec(测试点)

describe("A suite",function(){
      it("contains spec with an expectation",function(){
            expect(true).toBe(true);
       })
})

4. 常用断言

4.1 toEqual

深相等,对于目的的话,会比较对象的各种属性。对于数组来说,会相比数组中各个成分。

图片 20

  describe("The 'toEqual' matcher", function() {

    it("works for simple literals and variables", function() {
      var a = 12;
      expect(a).toEqual(12);
    });

    it("should work for objects", function() {
      var foo = {
        a: 12,
        b: 34
      };
      var bar = {
        a: 12,
        b: 34
      };
      expect(foo).toEqual(bar);
    });
  });

图片 21

 

4.2 toBe

对此目标,引用相等。对于值,值非常。

pass: actual === expected

例如

  it("and has a positive case", function() {
    expect(true).toBe(true);
  });

 

4.3 toBeTruthy

是或不是为真。

  it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
    var a, foo = "foo";

    expect(foo).toBeTruthy();
    expect(a).not.toBeTruthy();
  });

 

4.4 toBeFalsy

是还是不是为假。

  it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
    var a, foo = "foo";

    expect(a).toBeFalsy();
    expect(foo).not.toBeFalsy();
  });

 

4.5 toBeDefined

是否定义过

  it("creates spies for each requested function", function() {
    expect(tape.play).toBeDefined();
    expect(tape.pause).toBeDefined();
    expect(tape.stop).toBeDefined();
    expect(tape.rewind).toBeDefined();
  });

 

4.6 toBeUndefined

一直不定义

图片 22

  it("The `toBeUndefined` matcher compares against `undefined`", function() {
    var a = {
      foo: "foo"
    };

    expect(a.foo).not.toBeUndefined();
    expect(a.bar).toBeUndefined();
  });

图片 23

 

4.7 toBeNull

图片 24

  it("The 'toBeNull' matcher compares against null", function() {
    var a = null;
    var foo = "foo";

    expect(null).toBeNull();
    expect(a).toBeNull();
    expect(foo).not.toBeNull();
  });

图片 25

 

4.9 toBeGreaterThan

图片 26

  it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
    var pi = 3.1415926,
      e = 2.78;

    expect(pi).toBeGreaterThan(e);
    expect(e).not.toBeGreaterThan(pi);
  });

图片 27

 

4.10 toBeLessThan

图片 28

  it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
    var pi = 3.1415926,
      e = 2.78;

    expect(e).toBeLessThan(pi);
    expect(pi).not.toBeLessThan(e);
  });

图片 29

 

4.11 toBeCloseTo

图片 30

  it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
    var pi = 3.1415926,
      e = 2.78;

    expect(pi).not.toBeCloseTo(e, 2);
    expect(pi).toBeCloseTo(e, 0);
  });

图片 31

 

4.12 toContain

集合中是不是包括。

  it("The 'toContain' matcher is for finding an item in an Array", function() {
    var a = ["foo", "bar", "baz"];

    expect(a).toContain("bar");
    expect(a).not.toContain("quux");
  });

 

4.13 toMatch

正则表明式的相当

图片 32

  it("The 'toMatch' matcher is for regular expressions", function() {
    var message = "foo bar baz";

    expect(message).toMatch(/bar/);
    expect(message).toMatch("bar");
    expect(message).not.toMatch(/quux/);
  });

图片 33

 

4.14 toThrow

检测是还是不是抛出相当

图片 34

  it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
    var foo = function() {
      return 1 + 2;
    };
    var bar = function() {
      return a + 1;
    };

    expect(foo).not.toThrow();
    expect(bar).toThrow();
  });

图片 35

 

4.15 toHaveBeenCalled

4.16 toHaveBeenCalledWith

是还是不是调用过。

图片 36

describe("A spy", function() {
  var foo, bar = null;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      }
    };

    spyOn(foo, 'setBar');

    foo.setBar(123);
    foo.setBar(456, 'another param');
  });

  it("tracks that the spy was called", function() {
    expect(foo.setBar).toHaveBeenCalled();
  });

  it("tracks all the arguments of its calls", function() {
    expect(foo.setBar).toHaveBeenCalledWith(123);
    expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
  });

  it("stops all execution on a function", function() {
    expect(bar).toBeNull();
  });
});

              jasmine-html.js 测试页面 Dom 操作文件,

it方法

jasmine使用it来定义spec(测试点),it方法很像describe,同样有多少个参数,3个String,二个function。String用来叙述测试点(spec),function就是具体的测试代码;
一个测试点(sepc)可以分包多个expectations(断言)

              jasmine.js jasmine宗旨文件

expectations

断言以expect语句来代表,重临ture或false;
expect有1个参数,
代表测试的实际值,它和表示特出规则的Matcher链接在同步,Matcher带有期望值;
万事的预知重返true,这么些测试点才通过,只要有多个预知重回false,测试点不通过

describe("A suite is just a function",function(){
     var a;
     var b;
     it("and so is a spec",function(){
        a=true;
        b=false;
        expect(a).toBe(true);
        expect(b).toBe(true);
    })
 })

    spec 文件夹 :    PlayerSpec.js  单元测试文件

Matchers

Matcher达成了断言的相比较操作,将expectation传入的实际值和Matcher传入的期望值举办相比,得出断言的结果true
or false;

否定断言:任何Matcher都能经过在expect调用Matcher前增加
not来具体否定断言;

describe("Included matchers:", function() {

        it("The 'toBe' matcher compares with ===", function() {
            var a = 12;
            var b = a;

            expect(a).toBe(b);
            expect(a).not.toBe(null);
        });  
        //上面的例子,比较a、b是否相等;验证a是否不是空。 

        it("should work for objects", function() {
            var foo = {
                a: 12,
                b: 34
            };
            var bar = {
                a: 12,
                b: 34
            };
            expect(foo).toEqual(bar);
        });
        //上面的例子比较了两个对象是否相等
    });

    it("The 'toMatch' matcher is for regular expressions", function() {
        var message = 'foo bar baz';

        expect(message).toMatch(/bar/);
        expect(message).toMatch('bar');
        expect(message).not.toMatch(/quux/);
    });
    //也可以使用正则表达式
    it("The 'toBeDefined' matcher compares against `undefined`", function() {
        var a = {
            foo: 'foo'
        };

        expect(a.foo).toBeDefined();
        expect(a.bar).not.toBeDefined();
    });
    //验证变量是否被定义  

    it("The 'toBeNull' matcher compares against null", function() {
        var a = null;
        var foo = 'foo';

        expect(null).toBeNull();
        expect(a).toBeNull();
        expect(foo).not.toBeNull();
    });
    //验证是否为空

    it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
        var a, foo = 'foo';

        expect(foo).toBeTruthy();
        expect(a).not.toBeTruthy();
    });

    it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
        var a, foo = 'foo';

        expect(a).toBeFalsy();
        expect(foo).not.toBeFalsy();
    });
    //变量是否能够转化成boolean变量? 不太确定

    it("The 'toContain' matcher is for finding an item in an Array", function() {
        var a = ['foo', 'bar', 'baz'];

        expect(a).toContain('bar');
        expect(a).not.toContain('quux');
    });
    //是否包含
    it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
        var pi = 3.1415926, e = 2.78;

        expect(e).toBeLessThan(pi);
        expect(pi).not.toBeLessThan(e);
    });

    it("The 'toBeGreaterThan' is for mathematical comparisons", function() {
        var pi = 3.1415926, e = 2.78;

        expect(pi).toBeGreaterThan(e);
        expect(e).not.toBeGreaterThan(pi);
    });
    //数学大小的比较

    it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
    var pi = 3.1415926, e = 2.78;

    expect(pi).not.toBeCloseTo(e, 2);
    expect(pi).toBeCloseTo(e, 0);
    });
    //两个数值是否接近,这里接近的意思是将pi和e保留一定小数位数后,是否相等。(一定小数位数:默认为2,也可以手动指定)

    it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
        var foo = function() {
        return 1 + 2;
        };
        var bar = function() {
            return a + 1;
        };

        expect(foo).not.toThrow();
        expect(bar).toThrow();
        });
    }); 
    //测试一个方法是否抛出异常  

          SpecHelper.js    jasmine 断言扩充文件(自定义
Matcher)

Setup和Teardown方法

为了减弱重复性的代码,jasmine提供了beforeEachafterEachbeforeAllafterAll方法。

  • beforeEach() :在describe函数中各类Spec执行从前实施;
  • afterEach() :在describe函数中各种Spec执行之后执行;
  • beforeAll() :在describe函数中兼有的Specs执行从前实施,且只进行两遍
  • afterAll () : 在describe函数中具有的Specs执行之后执行,且只举行四遍

describe("A spec (with setup and tear-down)", function () {
    var foo;    
    //beforeAll 在所有的it方法执行之前执行一次
    beforeAll(function () { 
           foo = 1;       
          console.log("beforeAll run");   
   });    

   //afterAll 在所有的it方法执行之后执行一次
    afterAll(function () {       
        foo = 0;        
        console.log("afterAll run");   
    });   

  //beforeEach 在每个it方法执行之前都执行一次
   beforeEach(function () {        
        console.log("beforeEach run");   
   });    

 //afterEach 在每个it方法执行之后都执行一次
  afterEach(function () {        
      console.log("afterEach run");    
  });    

  it("is just a function,so it can contain any code", function () {       
       expect(foo).toEqual(1);    
  });   

  it("can have more than one expectation", function () {
       expect(foo).toEqual(1);
       expect(true).toEqual(true);    
  });
});

地点代码在浏览器控制台的输出

图片 37

beforeEach,beforeAll等方法执行顺序输出

很明显

  • beforeAll和afterAll在三个it执行前后,总的只进行了四遍;
  • beforeEach,afterEach在历次it执行前后,都实施了两次,所以结果打印了一遍beforeEach run
    afterEach run

    src 文件夹 ,上面是被测试的 js 文件。  SpecRunner.html
为测试结果页面。

describe函数的嵌套

describe函数可以嵌套,嵌套中的每层都可以定义Specs(测试点)、beforeEach以及afterEach函数;

履行内层Spec时,会按嵌套由外到内的逐条执行各样beforeEach函数(所以内层Spec可以访问外层Spec中的beforeEach中的数据),其余当内层Spec执行完后,会按由内到外的顺序执行各类afterEach函数;

describe("A spec", function () {
    var foo;
    beforeAll(function () {
        console.log("outer beforeAll");
    });

    afterAll(function () {
        console.log("outer afterAll");
    });

    beforeEach(function () {
        foo = 0;
        foo += 1;
        console.log("outer beforeEach");
    });

    afterEach(function () {
        foo = 0;
        console.log("outer afterEach");
    });

    it("is just a function, so it can contain any code", function () {
        expect(foo).toEqual(1);
    });   

   it("can have more than one expectation", function () {
        expect(foo).toEqual(1);
        expect(true).toEqual(true);
    });

    describe("nested inside a second describe", function () { 
       var bar;
        beforeAll(function () {
            console.log("inner beforeAll");
        });
        beforeEach(function () {
            bar = 1;
            console.log("inner beforeEach")
        });
        it("can reference both scopes as needed", function () {
            expect(foo).toEqual(bar);
        })
    })
});

一如既往,代码在浏览器控制台有出口

图片 38

嵌套describe,beforeEach、befroreAll等施行各类输出

  SpecRunner.html 代码,注意js文件加载顺序

自定义Matchers

自定义Matcher是三个函数,该函数再次来到3个闭包,该闭包实质是三个compare函数,compare收受壹个参数:actual
value(expect传入的实际值)和expected value(matcher函数传来的期望值);

compare函数必须回到三个带pass属性的Object,pass属性值是壹个boolean值,表示Matcher的结果,换句话说,实际值和期望值比较的结果,存放在pass属性中;

测试失败的提醒新闻可以经过Object的message属性来定义,若是没概念message消息再次回到,则会jasmine会生成3个暗许的错误新闻提醒;

var customMatchers = {
    toBeGoofy: function (util, customEqualityTesters) {
        return {
            compare: function (actual, expected) {
                if (expected === undefined) {
                    expected = '';
                }
                console.log(util);
                console.log(customEqualityTesters);
                var result = {}; 

                //比较结果true or false 通过pass 属性值返回
                result.pass = util.equals(actual.hyuk, "gawrsh" + expected, customEqualityTesters); 

                //messge定义
                if (result.pass) {
                    result.message = "Expected" + actual + "not to be quite so goofy";
                } else {
                    result.message = "Expected" + actual + "to be goofy,but it was not very goofy"; 
               }
                return result;
            }
        };
  }};

自定义Matcher构造函数接受三个参数,util:给Matcher使用的一组工具函数(equals,contains,buildFailureMessage)
;customEqualityTesters:调用util.equals时索要传入,仅此而已

   

自定义Matchers的使用

在概念完Matcher之后,就是选拔它了。有三种接纳Matcher的方法:

  • 将Matcher函数添加到特定describe函数的beforeEach中,方便该describe函数中的Spec都能调用得到它。其他非嵌套describe中的Spec是力不从心调用到它的;

describe("Custom matcher: 'toBeGoofy'", function() {
     beforeEach(function() {
        jasmine.addMatchers(customMatchers);
 });

 it("can take an 'expected' parameter", function() {
       expect({
            hyuk: 'gawrsh is fun'
          }).toBeGoofy(' is fun');
    });
});
  • 除此以外一种是将Matcher函数添加到全局beforeEach函数中,那样全部的Suites中的全体的Specs,都得以动用该Matcher。上面的例证引用自官方example

//定义
beforeEach(function () {
  jasmine.addMatchers({
    toBePlaying: function () {
      return {
        compare: function (actual, expected) {
          var player = actual; 
         return {
            pass: player.currentlyPlayingSong === expected && player.isPlaying
          };
        }
      };
    }
  });
});

//应用
describe("Player", function() {
   it("should be able to play a Song", function() {
     player.play(song);
     //demonstrates use of custom matcher
     expect(player).toBePlaying(song);
 });

 describe("when song has been paused", function() {
     it("should indicate that the song is currently paused",   function() {
     // demonstrates use of 'not' with a custom matcher
       expect(player).not.toBePlaying(song);
     });
)};
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner v2.5.2</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.5.2/jasmine_favicon.png">
  <link rel="stylesheet" href="lib/jasmine-2.5.2/jasmine.css">

  <script src="lib/jasmine-2.5.2/jasmine.js"></script>
  <script src="lib/jasmine-2.5.2/jasmine-html.js"></script>
  <script src="lib/jasmine-2.5.2/boot.js"></script>

  <!-- include source files here... -->
  <script src="src/Player.js"></script>
  <script src="src/Song.js"></script>

  <!-- include spec files here... -->
  <script src="spec/SpecHelper.js"></script>
  <script src="spec/PlayerSpec.js"></script>

</head>

<body>
</body>
</html>
我的jasmine gitstart

https://github.com/unnKoel/jasmine-gitstart

 

参考

github
jasmine

有关前端开发谈谈单元测试

JavaScript单元测试框架-Jasmine
Javascript测试框架Jasmine(四):自定义Matcher
jasmine测试框架简介
JavaScript 单元测试框架:Jasmine初探

    大家直接运转 SpecRunner.html  测试结果如下:

图片 39

八个 specs,0个全盘皆输,全体透过。在 PlayerSpec.js
里添加2个Suite,看看报错是何等的。

describe("error test",function() {
    it("Here the test does not pass",function() {
        expect(1).toBe(2);
    });
})

 图片 40

哈哈哈,测试未通过,看到没,那里显得了详尽的错误音讯。

   jasmine 语法详解

        首先明白多少个概念: Suite 指3个测试集,
describe方法标明着七个测试集。

            Spec 表示测试用例,jasmine中用艺术it来初始specs。

            3个 Suite可以分包三个 Spec,三个 spec
可以涵盖三个 expections 断言

  示例1

  

//测试集 开始于调用全局Jasmine函数describe,有两个参数:一个字符串和一个函数。 
//该字符串是specs(测试用例)单元测试的名称或标题 - 通常是被测试的。 该函数是一个实现单元测试的代码块。

describe("A suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
  });
});

 

  示例2        包涵五个断言

 

describe("A suite is just a function", function() {
  var a;

  it("and so is a spec", function() {
    a = true;

    expect(a).toBe(true);
  });
});



describe("The 'toBe' matcher compares with ===", function() {
  it("and has a positive case", function() {
    expect(true).toBe(true);
  });
  it("and can have a negative case", function() {
    expect(false).not.toBe(true);
  });
});

 

 示例3  
常用语法,describe嵌套

describe("Included matchers:", function() {

  it("The 'toBe' matcher compares with ===", function() {
    var a = 12;
    var b = a;

    expect(a).toBe(b);
    expect(a).not.toBe(null);
  });

  describe("The 'toEqual' matcher", function() {

    it("works for simple literals and variables", function() {
      var a = 12;
      expect(a).toEqual(12);
    });

    it("should work for objects", function() {
      var foo = {
        a: 12,
        b: 34
      };
      var bar = {
        a: 12,
        b: 34
      };
      expect(foo).toEqual(bar);
    });
  });

  it("The 'toMatch' matcher is for regular expressions", function() {
    var message = "foo bar baz";

    expect(message).toMatch(/bar/);
    expect(message).toMatch("bar");
    expect(message).not.toMatch(/quux/);
  });

  it("The 'toBeDefined' matcher compares against `undefined`", function() {
    var a = {
      foo: "foo"
    };

    //已定义
    expect(a.foo).toBeDefined();
    expect(a.bar).not.toBeDefined();
  });

  it("The `toBeUndefined` matcher compares against `undefined`", function() {
    var a = {
      foo: "foo"
    };

    //未定义
    expect(a.foo).not.toBeUndefined();
    expect(a.bar).toBeUndefined();
  });

  it("The 'toBeNull' matcher compares against null", function() {
    var a = null;
    var foo = "foo";

    expect(null).toBeNull();
    expect(a).toBeNull();
    expect(foo).not.toBeNull();
  });

  it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
    var a, foo = "foo";

    expect(foo).toBeTruthy();
    expect(a).not.toBeTruthy();
  });

  it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
    var a, foo = "foo";

    expect(a).toBeFalsy();
    expect(foo).not.toBeFalsy();
  });

  describe("The 'toContain' matcher", function() {
    it("works for finding an item in an Array", function() {
      var a = ["foo", "bar", "baz"];

      //包含
      expect(a).toContain("bar");
      expect(a).not.toContain("quux");
    });

    it("also works for finding a substring", function() {
      var a = "foo bar baz";

      expect(a).toContain("bar");
      expect(a).not.toContain("quux");
    });
  });

  it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
    var pi = 3.1415926,
      e = 2.78;

    //小于
    expect(e).toBeLessThan(pi);
    expect(pi).not.toBeLessThan(e);
  });

  it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
    var pi = 3.1415926,
      e = 2.78;

    //大于
    expect(pi).toBeGreaterThan(e);
    expect(e).not.toBeGreaterThan(pi);
  });

  it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
    var pi = 3.1415926,
      e = 2.78;

    //临近  是比较两个值是否足够接近(不一定要相等)
    //源码:pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
    //即  pi - e 的绝对值  是否 小于  10 的 X(2) 次方  / 2
    //以 expect(pi).not.toBeCloseTo(e, 3); 为例,就是 pi 跟 e 的差 绝对值 ,是否小于 1/1000 除以 2 ,即 0.0005  

    expect(pi).not.toBeCloseTo(e, 2);
    expect(pi).toBeCloseTo(e, 0);
  });

  it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
    var foo = function() {
      return 1 + 2;
    };
    var bar = function() {
      return a + 1;
    };

    //是否引发异常
    expect(foo).not.toThrow();
    expect(bar).toThrow();
  });

  it("The 'toThrowError' matcher is for testing a specific thrown exception", function() {
    var foo = function() {
      throw new TypeError("foo bar baz");
    };

    //是否抛出指定错误
    expect(foo).toThrowError("foo bar baz");
    expect(foo).toThrowError(/bar/);
    expect(foo).toThrowError(TypeError);
    expect(foo).toThrowError(TypeError, "foo bar baz");
  });
});


//手动制造一个断言失败

//fail函数使specs(测试用例)失败。 它可以将失败消息或Error对象作为参数。
describe("A spec using the fail function", function() {
  var foo = function(x, callBack) {
    if (x) {
      callBack();
    }
  };

  it("should not call the callBack", function() {
    foo(false, function() {  
      fail("Callback has been called");
    });
  });
});


//分组相关规则带
//describe 函数用于对相关specs(测试用例)进行分组。 string参数用于命名specs的集合,并且将与specs连接以构成spec的全名。 
//这有助于在 测试集 找到规则。 如果你很好地命名他们,你的规则读为传统的BDD风格的完整句子。

describe("A spec", function() {
  it("is just a function, so it can contain any code", function() {
    var foo = 0;
    foo += 1;

    expect(foo).toEqual(1);
  });

  it("can have more than one expectation", function() {
    var foo = 0;
    foo += 1;

    expect(foo).toEqual(1);
    expect(true).toEqual(true);
  });
});

 示例4  
beforeEach,afterEach,beforeAll和afterAll函数

//顾名思义,beforeEach函数在调用它的describe中的每个 spec 之前调用一次,afterEach函数在每个spec之后调用一次。
//这里是同一组specs(测试用例)写得有点不同。 被测变量定义在顶层作用域 - 描述块和初始化代码被移入一个beforeEach函数。 
//afterEach函数在继续之前重置变量。 
describe("A spec using beforeEach and afterEach", function() {
  var foo = 0;

  beforeEach(function() {
    foo += 1;
  });

  afterEach(function() {
    foo = 0;
  });

  it("is just a function, so it can contain any code", function() {
    expect(foo).toEqual(1);
  });

  it("can have more than one expectation", function() {
    expect(foo).toEqual(1);
    expect(true).toEqual(true);
  });
});




//beforeAll函数仅在describe中的所有specs(测试用例)运行之前调用一次,并且afterAll函数在所有specs(测试用例)完成后调用。 
//这些功能可用于加快测试集 的昂贵设置和拆卸。
//但是,要小心使用beforeAll和afterAll! 由于它们不在specs(测试用例)之间重置,很容易在specs(测试用例)之间意外泄漏状态,
//使它们错误地通过或失败。  注意跟 beforeEach 的区别,
//如果 在第1个 it 里改变了 foo 的值,第2个 it 的值就不是 初始化时的值了
describe("A spec using beforeAll and afterAll", function() {
  var foo;

  beforeAll(function() {
    foo = 1;
  });

  afterAll(function() {
    foo = 0;
  });

  it("sets the initial value of foo before specs run", function() {
    expect(foo).toEqual(1);
    foo += 1;
  });

  it("does not reset foo between specs", function() {
    expect(foo).toEqual(2);
  });
});

 示例5 
this关键字共享变量,嵌套describe

//this关键字

//另一种在beforeEach,it和afterEach之间共享变量的方法是通过this关键字。 
//每个spec的beforeEach / it / afterEach都将这个作为同一个空对象,对于下一个spec的beforeEach / it / afterEach设置为空。
describe("A spec", function() {
  beforeEach(function() {
    this.foo = 0;
  });

  it("can use the `this` to share state", function() {
    expect(this.foo).toEqual(0);
    this.bar = "test pollution?";
  });

  it("prevents test pollution by having an empty `this` created for the next spec", function() {
    expect(this.foo).toEqual(0);
    //注意这里的区别 undefined
    expect(this.bar).toBe(undefined);
  });
});

//嵌套describe , describe 里嵌套 describe

//调用describe可以嵌套,在任何级别定义specs(测试用例)。 这允许一个单元测试被组成一个函数树。 
//在执行specs(测试用例)之前,Jasmine沿着树顺序执行每个beforeEach函数。 
//在specs(测试用例)执行后,Jasmine类似地遍历 afterEach 函数。
describe("A spec", function() {
  var foo;

  beforeEach(function() {
    foo = 0;
    foo += 1;
  });

  afterEach(function() {
    foo = 0;
  });

  it("is just a function, so it can contain any code", function() {
    expect(foo).toEqual(1);
  });

  it("can have more than one expectation", function() {
    expect(foo).toEqual(1);
    expect(true).toEqual(true);
  });

  describe("nested inside a second describe", function() {
    var bar;

    beforeEach(function() {
      bar = 1;
    });

    it("can reference both scopes as needed", function() {
      expect(foo).toEqual(bar);
    });
  });
});

 示例6   Pending
待定规则

//待定规则
//待处理的规则不会运行,但它们的名称将在结果中显示为待处理。 
describe("Pending specs", function() {


  //任何用xit声明的spec都被标记为pending。
  xit("can be declared 'xit'", function() {
    expect(true).toBe(false);
  });

 //在没有函数体的情况下声明的任何specs(测试用例)也将在结果中被标记为待处理
  it("can be declared with 'it' but without a function");

//pending() 如果你在specs(测试用例)体中任何地方调用该函数,无论预期如何,specs(测试用例)将被标记为待定。 
//pending()函数接受一个字符串参数,该参数会在结果集中显示在 PENDING WITH MESSAGE:之后,作为为何被Pending的原因。
 it("can be declared by calling 'pending' in the spec body", function() {
    expect(true).toBe(false);
    pending('this is why it is pending');
  });
});

 示例7  Spies
对象监控

// Spies

//Jasmine有 spies(监控) 双重测试功能。   spy 可以存根任何函数并跟踪对它和所有参数的调用。 
//spy 只存在于描述或其定义的块中,并且将在每个specs(测试用例)之后删除。 有特殊的匹配器与 spies 交互。 
//Jasmine 2.0的语法已更改。

describe("A spy", function() {
  var foo, bar = null;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      }
    };

    spyOn(foo, 'setBar');
    //spyOn(foo, 'setBar').and.callThrough();

    foo.setBar(123);
    foo.setBar(456, 'another param');
  });

  //如果调用 Spies ,toHaveBeenCalled匹配器将返回true。
  //是否被调用
  it("tracks that the spy was called", function() {
    expect(foo.setBar).toHaveBeenCalled();
  });

  //如果 Spies 被调用了指定的次数,toHaveBeenCalledTimes匹配器将通过。
  it("tracks that the spy was called x times", function() {
    expect(foo.setBar).toHaveBeenCalledTimes(2);
  });

  //如果参数列表匹配任何记录的调用到 Spies ,toHaveBeenCalledWith匹配器将返回true。
  it("tracks all the arguments of its calls", function() {
    expect(foo.setBar).toHaveBeenCalledWith(123);
    expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
  });

  it("stops all execution on a function", function() {

      //有没有感到奇怪,beforeEach 里调用了 foo.setBar(),这里为什么 bar 的值为 null ?? 
      //原因是 spyOn(foo, 'setBar'); 并不会去调用 真实的 foo.setBar()函数,只是调用了 Jasmine 保存的这个函数的 存根,不会影响到实际的值
      //如果这样写 spyOn(foo, 'setBar').and.callThrough(); 就会调用真实的 foo.setBar()函数了,bar的值也会跟随改变
      expect(bar).toBeNull();
  });
});


// Spies :and.callThrough
//通过使用and.callThrough链接 Spies , Spies 仍然会跟踪对它的所有调用,但此外它将委派给实际的实现。
describe("A spy, when configured to call through", function() {
  var foo, bar, fetchedBar;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      },
      getBar: function() {
        return bar;
      }
    };

    spyOn(foo, 'getBar').and.callThrough();

    foo.setBar(123);
    fetchedBar = foo.getBar();
  });

  it("tracks that the spy was called", function() {
    expect(foo.getBar).toHaveBeenCalled();
  });

  it("should not affect other functions", function() {
    expect(bar).toEqual(123);
  });

  it("when called returns the requested value", function() {
      //这里 fetchedBar 有值
      //这就是 spyOn(foo, 'getBar').and.callThrough() 跟 spyOn(foo, 'getBar') 的区别
      expect(fetchedBar).toEqual(123);
  });
});



// Spies :and.returnValue

//通过使用and.returnValue链接 Spies ,所有对函数的调用都将返回特定的值。
describe("A spy, when configured to fake a return value", function() {
  var foo, bar, fetchedBar;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      },
      getBar: function() {
        return bar;
      }
    };

    spyOn(foo, "getBar").and.returnValue(745);

    foo.setBar(123);
    //所有调用 foo.getBar() 函数都返回 745
    fetchedBar = foo.getBar();
  });

  it("tracks that the spy was called", function() {
    expect(foo.getBar).toHaveBeenCalled();
  });

  it("should not affect other functions", function() {
    expect(bar).toEqual(123);
  });

  it("when called returns the requested value", function() {
    expect(fetchedBar).toEqual(745);
  });
});


// specs :and.returnValues

//通过使用and.returnValues链接 specs ,所有对函数的调用将按顺序返回特定的值,
//直到它到达返回值列表的结尾,此时它将返回未定义的所有后续调用。
describe("A spy, when configured to fake a series of return values", function() {
  var foo, bar;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      },
      getBar: function() {
        return bar;
      }
    };

    spyOn(foo, "getBar").and.returnValues("fetched first", "fetched second");

    foo.setBar(123);
  });

  it("tracks that the spy was called", function() {
    //返回调用次数 对应的 参数数组 下标的值
    foo.getBar(123); 
    expect(foo.getBar).toHaveBeenCalled();
  });

  it("should not affect other functions", function() {
    //不要迷惑了,赋值是在 beforeEach 里做的,不是 foo.getBar(123); 
    expect(bar).toEqual(123);
  });

  it("when called multiple times returns the requested values in order", function() {
    //返回调用次数 对应的 参数数组 下标的值
    expect(foo.getBar()).toEqual("fetched first");
    expect(foo.getBar()).toEqual("fetched second");
    expect(foo.getBar()).toBeUndefined();
  });
});


// specs :and.callFake
//通过使用and.callFake链接 specs ,所有对 specs 的调用都将委派给提供的函数。
describe("A spy, when configured with an alternate implementation", function() {
  var foo, bar, fetchedBar;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      },
      getBar: function() {
        return bar;
      }
    };


    //如果被窥探的函数接收到假的需要的参数,你可以得到那些
    spyOn(foo, "getBar").and.callFake(function(arguments, can, be, received) {
      return 1001;
    });

    foo.setBar(123);
    fetchedBar = foo.getBar();
  });

  it("tracks that the spy was called", function() {
    expect(foo.getBar).toHaveBeenCalled();
  });

  it("should not affect other functions", function() {
    expect(bar).toEqual(123);
  });

  it("when called returns the requested value", function() {
    expect(fetchedBar).toEqual(1001);
  });
});



// specs :and.throwError

//通过使用and.throwError链接 specs ,所有对 specs 的调用都将抛出指定的值作为错误。
describe("A spy, when configured to throw an error", function() {
  var foo, bar;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      }
    };

    spyOn(foo, "setBar").and.throwError("quux");
  });

  it("throws the value", function() {
    expect(function() {
      foo.setBar(123)
    }).toThrowError("quux");
  });
});


// specs :and.stub

//当调用策略用于 specs 时,可以随时使用and.stub返回原始的存根行为。 
describe("A spy", function() {
  var foo, bar = null;

  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      }
    };

    spyOn(foo, 'setBar').and.callThrough();
  });

  it("can call through and then stub in the same spec", function() {
    foo.setBar(123);
    expect(bar).toEqual(123);

    foo.setBar.and.stub();
    bar = null;

    foo.setBar(123);
    //返回原始的存根
    expect(bar).toBe(null);
  });
});

 在地点那段代码里 ,要小心 Spies
:and.callThrough  的用法  注意代码  spyOn(foo, ‘getBar’).and.callThrough(); 跟
spyOn(foo, ‘getBar’);  的不一致  spyOn(foo, ‘getBar’).and.callThrough()
会调用实例方法

暴发实际的熏陶,而 spyOn(foo,
‘getBar’);  只是调用了 贾斯敏 保存的这几个函数的
存根,不会潜移默化到实在的值 ,倘使没看领会请仔细看代码上自己添加的笺注。

 

以身作则8   其余质量

describe("A spy", function() {
  var foo, bar = null;

  //每个对 specs 的调用都会被跟踪并在calls属性上公开
  beforeEach(function() {
    foo = {
      setBar: function(value) {
        bar = value;
      }
    };

    spyOn(foo, 'setBar');
  });



//.calls.any():如果spy没有被调用,则返回false,如果至少有一个调用发生,则返回true
it("tracks if it was called at all", function() {
    expect(foo.setBar.calls.any()).toEqual(false);
    foo.setBar();
    expect(foo.setBar.calls.any()).toEqual(true);
  });


  //.calls.count():返回调用 specs 的次数
  it("tracks the number of times it was called", function() {
    expect(foo.setBar.calls.count()).toEqual(0);

    foo.setBar();
    foo.setBar();

    expect(foo.setBar.calls.count()).toEqual(2);
  });



//.calls.argsFor(index):返回传递给调用号索引的参数
it("tracks the arguments of each call", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.argsFor(0)).toEqual([123]);
    expect(foo.setBar.calls.argsFor(1)).toEqual([456, "baz"]);
  });

  //.calls.allArgs():返回所有调用的参数
  it("tracks the arguments of all calls", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.allArgs()).toEqual([[123],[456, "baz"]]);
  });

//.calls.all():返回上下文(this)和传递所有调用的参数
it("can provide the context and arguments to all calls", function() {
    foo.setBar(123);

    expect(foo.setBar.calls.all()).toEqual([{object: foo, args: [123], returnValue: undefined}]);
  });


  //.calls.mostRecent():返回上一次调用的上下文(this)和参数
  it("has a shortcut to the most recent call", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.mostRecent()).toEqual({object: foo, args: [456, "baz"], returnValue: undefined});
  });

//.calls.first():返回上下文(this)和第一次调用的参数
 it("has a shortcut to the first call", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.first()).toEqual({object: foo, args: [123], returnValue: undefined});
  });


  //当检查来自all(),mostRecent()和first()的返回时,当调用 specs 时,object属性被设置为this的值。
  it("tracks the context", function() {
    var spy = jasmine.createSpy('spy');
    var baz = {
      fn: spy
    };
    var quux = {
      fn: spy
    };
    baz.fn(123);
    quux.fn(456);

    //.object 返回的this ,即调用对象
    expect(spy.calls.first().object).toBe(baz);
    expect(spy.calls.mostRecent().object).toBe(quux);
  });


  //.calls.reset():清除 specs 的所有跟踪
  it("can be reset", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.any()).toBe(true);
    foo.setBar.calls.reset();
    expect(foo.setBar.calls.any()).toBe(false);
  });
});


// specs :createSpy
//当没有一个函数来监视,jasmine.createSpy可以创建一个“裸” specs 。 
//这个 specs 作为任何其他 specs  - 跟踪调用,参数等,但其没有实现。  specs 是JavaScript对象,可以这样使用。
describe("A spy, when created manually", function() {
  var whatAmI;

  beforeEach(function() {
    whatAmI = jasmine.createSpy('whatAmI');
    whatAmI("I", "am", "a", "spy");
  });

  it("is named, which helps in error reporting", function() {
    expect(whatAmI.and.identity()).toEqual('whatAmI');
  });

  it("tracks that the spy was called", function() {
    expect(whatAmI).toHaveBeenCalled();
  });

  it("tracks its number of calls", function() {
    expect(whatAmI.calls.count()).toEqual(1);
  });

  it("tracks all the arguments of its calls", function() {
    expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy");
  });

  it("allows access to the most recent call", function() {
    expect(whatAmI.calls.mostRecent().args[0]).toEqual("I");
  });
});



// specs :createSpyObj
//为了创建一个有多个 specs 的模拟,使用jasmine.createSpyObj并传递一个字符串数组。 它返回一个对象,它具有属于 specs 的每个字符串的属性。
describe("Multiple spies, when created manually", function() {
  var tape;

  beforeEach(function() {
    tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);
    tape.play();
    tape.pause();
    tape.rewind(0);
  });

  it("creates spies for each requested function", function() {
    expect(tape.play).toBeDefined();
    expect(tape.pause).toBeDefined();
    expect(tape.stop).toBeDefined();
    expect(tape.rewind).toBeDefined();
  });

  it("tracks that the spies were called", function() {
    expect(tape.play).toHaveBeenCalled();
    expect(tape.pause).toHaveBeenCalled();
    expect(tape.rewind).toHaveBeenCalled();
    expect(tape.stop).not.toHaveBeenCalled();
  });

  it("tracks all the arguments of its calls", function() {
    expect(tape.rewind).toHaveBeenCalledWith(0);
  });
});

//匹配所有与jasmine.any 
describe("jasmine.anything", function() {

  //如果实际值不为null或未定义,jasmine.anything返回true。
  it("matches anything", function() {
    expect(1).toEqual(jasmine.anything());
  });

  describe("when used with a spy", function() {
    it("is useful when the argument can be ignored", function() {
      var foo = jasmine.createSpy('foo');
      foo(12, function() {
        return false;
      });

      expect(foo).toHaveBeenCalledWith(12, jasmine.anything());
    });
  });
});

//与jasmine.objectContaining的部分匹配
//jasmine.objectContaining是用于期望在实际中只关心某些键/值对的时候。
describe("jasmine.objectContaining", function() {
  var foo;

  beforeEach(function() {
    foo = {
      a: 1,
      b: 2,
      bar: "baz"
    };
  });

  it("matches objects with the expect key/value pairs", function() {

    //只比对bar
    expect(foo).toEqual(jasmine.objectContaining({
      bar: "baz"
    }));

    expect(foo).not.toEqual(jasmine.objectContaining({
      c: 37
    }));

  });

  describe("when used with a spy", function() {
    it("is useful for comparing arguments", function() {
      var callback = jasmine.createSpy('callback');

      callback({
        bar: "baz"
      });

      expect(callback).toHaveBeenCalledWith(jasmine.objectContaining({
        bar: "baz"
      }));

      expect(callback).not.toHaveBeenCalledWith(jasmine.objectContaining({
        c: 37
      }));
    });
  });
});


//部分数组与jasmine.arrayContaining相匹配
//jasmine.arrayContaining用于那些期望只关心数组中的某些值的时候。
describe("jasmine.arrayContaining", function() {
  var foo;

  beforeEach(function() {
    foo = [1, 2, 3, 4];
  });

  it("matches arrays with some of the values", function() {
    expect(foo).toEqual(jasmine.arrayContaining([3, 1]));
    expect(foo).not.toEqual(jasmine.arrayContaining([6]));
  });

  describe("when used with a spy", function() {
    it("is useful when comparing arguments", function() {
      var callback = jasmine.createSpy('callback');

      callback([1, 2, 3, 4]);

      expect(callback).toHaveBeenCalledWith(jasmine.arrayContaining([4, 2, 3]));
      expect(callback).not.toHaveBeenCalledWith(jasmine.arrayContaining([5, 2]));
    });
  });
});


//字符串与jasmine.stringMatching匹配
//jasmine.stringMatching用于当你不想完全匹配较大对象中的字符串时,或者匹配 specs 预期中的字符串的一部分。
describe('jasmine.stringMatching', function() {
  it("matches as a regexp", function() {
    expect({foo: 'bar'}).toEqual({foo: jasmine.stringMatching(/^bar$/)});
    expect({foo: 'foobarbaz'}).toEqual({foo: jasmine.stringMatching('bar')});
  });

  describe("when used with a spy", function() {
    it("is useful for comparing arguments", function() {
      var callback = jasmine.createSpy('callback');

      callback('foobarbaz');

      expect(callback).toHaveBeenCalledWith(jasmine.stringMatching('bar'));
      expect(callback).not.toHaveBeenCalledWith(jasmine.stringMatching(/^bar$/));
    });
  });
});



//定制不对称等式测试器
//当您需要检查某个满足特定标准的条件,而不是严格相等时,您还可以通过提供具有asymmetricMatch函数的对象来指定自定义非对称等式测试器。
describe("custom asymmetry", function() {
  var tester = {
    asymmetricMatch: function(actual) {
      var secondValue = actual.split(',')[1];
      return secondValue === 'bar';
    }
  };

  it("dives in deep", function() {
    expect("foo,bar,baz,quux").toEqual(tester);
  });

  describe("when used with a spy", function() {
    it("is useful for comparing arguments", function() {
      var callback = jasmine.createSpy('callback');
      callback('foo,bar,baz');

      expect(callback).toHaveBeenCalledWith(tester);
    });
  });
});

 示例 9 
Jasmine 时钟

//Jasmine 时钟
//Jasmine 2.0的此语法已更改。 Jasmine时钟可用于测试时间相关代码。
describe("Manually ticking the Jasmine Clock", function() {
  var timerCallback;

 //它安装调用了 jasmine.clock()。安装在需要操纵时间的spec或suite。
  beforeEach(function() {
    timerCallback = jasmine.createSpy("timerCallback");
    jasmine.clock().install();
  });

 //完成恢复原始功能后,请务必卸载时钟。
 afterEach(function() {
    jasmine.clock().uninstall();
  });



  //模拟JavaScript超时函数
  //您可以使setTimeout或setInterval同步执行已注册的函数,只有当时钟在时间上向前跳过时。
  //要执行注册的函数,通过jasmine.clock()。tick函数延时时间,该函数 参数为 毫秒。
  it("causes a timeout to be called synchronously", function() {
    setTimeout(function() {
      timerCallback();
    }, 100);

    expect(timerCallback).not.toHaveBeenCalled();
    jasmine.clock().tick(101);

    expect(timerCallback).toHaveBeenCalled();
  });

  it("causes an interval to be called synchronously", function() {
    setInterval(function() {
      timerCallback();
    }, 100);

    expect(timerCallback).not.toHaveBeenCalled();

    jasmine.clock().tick(101);
    expect(timerCallback.calls.count()).toEqual(1);

    jasmine.clock().tick(50);
    expect(timerCallback.calls.count()).toEqual(1);

    jasmine.clock().tick(50);
    expect(timerCallback.calls.count()).toEqual(2);
  });


 //模拟日期
 //Jasmine时钟也可以用来模拟当前日期。
 describe("Mocking the Date object", function(){
    it("mocks the Date object and sets it to a given time", function() {
      var baseTime = new Date(2013, 9, 23);

      //如果你没有为mockDate提供基准时间,它将使用当前日期。
      jasmine.clock().mockDate(baseTime);

      jasmine.clock().tick(50);
      expect(new Date().getTime()).toEqual(baseTime.getTime() + 50);
    });
  });
});

 

示例 10  
异步辅助

//异步支持
//Jasmine 2.0的此语法已更改。 Jasmine还支持运行需要测试异步操作的specs(测试用例)。
describe("Asynchronous specs", function() {
  var value;

 //调用beforeAll,afterAll,beforeEach,afterEach和它可以接受一个可选的单个参数,当异步工作完成时,应该调用。
 beforeEach(function(done) {
    setTimeout(function() {
      value = 0;
      done();
    }, 1);
  });


  //在done函数在调用之前,这个specs(测试用例)不会开始。
  //这个specs(测试用例)将会等待 beforeEach 调用 done() 后执行。
  it("should support async execution of test preparation and expectations", function(done) {
    value++;
    expect(value).toBeGreaterThan(0);
    expect(value).toBe(1);//所以这里value 的值为1
    done();
  });


  //默认情况下,jasmine将等待5秒钟,异步specs(测试用例)在导致超时失败之前完成。 
  //如果超时在调用done之前超时,则当前specs(测试用例)将被标记为失败,并且单元测试执行将继续,如同调用完成。
  //如果特定规则应该更快失败或需要更多时间,可以通过向其传递超时值等来调整。
  //如果整个单元测试应该有不同的超时,则可以在任何给定描述之外全局设置jasmine.DEFAULT_TIMEOUT_INTERVAL。
  describe("long asynchronous specs", function() {
    beforeEach(function(done) {
      done();
    }, 1000);

    it("takes a long time", function(done) {
      setTimeout(function() {
        done();
      }, 9000);
    }, 10000);

    afterEach(function(done) {
      done();
    }, 1000);
  });


  //done.fail函数使specs(测试用例)失败,并指示它已完成
  describe("A spec using done.fail", function() {
    var foo = function(x, callBack1, callBack2) {
      if (x) {
        setTimeout(callBack1, 0);
      } else {
        setTimeout(callBack2, 0);
      }
    };

    it("should not call the second callBack", function(done) {
      foo(true,
        done,
        function() {
          done.fail("Second callback has been called");
        }
      );
    });
  });
});

 示例11 
自定义matcher 

//通常,项目将要封装用于跨多个规范的自定义匹配代码。 下面是如何创建一个Jasmine兼容的自定义匹配器。
//在其根部的自定义匹配器是比较函数,其获取实际值和期望值。 
//这个工厂被传递给Jasmine,理想的情况是调用beforeEach,并且在一个给定的调用中描述的所有规范的范围内。 
//定制匹配器在规格之间拆分。 工厂的名称将是在期望的调用的返回值上暴露的匹配器的名称。
var customMatchers = {

    //自定义匹配器工厂传递两个参数:util,它有一组用于匹配器使用的效用函数(见:matchersUtil.js用于当前列表)和customEqualityTesters,
    //如果util.equals被调用,则需要传递。 当调用匹配器时,可以使用这些参数。
    toBeGoofy: function (util, customEqualityTesters) {
        //工厂方法应该返回一个含有比较函数的对象,该函数将被调用以检查期望值。
        return {
            //比较函数第一个参数为实际值 ,第二个参数传递给匹配器本身的值(如果有的话)。
            compare: function (actual, expected) {

                //toBeGoofy接受一个可选的期望参数,所以如果不传递,在这里定义。
                if (expected === undefined) {
                    expected = '';
                }
                var result = {};

                if (result.pass) {
                    //如果未定义,期望将尝试为匹配器创建失败消息。 但是,如果返回值具有message属性,它将用于失败的期望。
                    result.message = "Expected " + actual + " not to be quite so goofy";
                } else {
                    //匹配成功,所以自定义失败消息应该出现在负期望的情况下 - 当期望与.not一起使用时。
                    result.message = "Expected " + actual + " to be goofy, but it was not very goofy";
                }
                return result;
            }
        };
    }
};




//调用代码
describe("Custom matcher: 'toBeGoofy'", function() { 
    beforeEach(function() {
        jasmine.addMatchers(customMatchers);
    });

    it("is available on an expectation", function () {
        expect({
            hyuk: 'gawrsh'
        }).toBeGoofy();
    });

    it("can take an 'expected' parameter", function () {
        expect({
            hyuk: 'gawrsh is fun'
        }).toBeGoofy('is fun');
    });

    it("can be negated", function () {
        expect({
            hyuk: 'this is fun'
        }).not.toBeGoofy();
    });
}); 

 看完上边的以身作则应该在项目中利用尚未怎么难点了。

相关文章