zoukankan      html  css  js  c++  java
  • 前端测试框架 jasmine 的使用

           最近的项目在使用AngulaJs,对JS代码的测试问题就摆在了面前。通过对比我们选择了 Karma  + jasmine ,使用 Jasmine做单元测试 ,Karma 自动化完成,当然了如果使用 Karma  + jasmine 前提是必须安装 Nodejs。

    安装好 Nodejs ,使用 npm 安装好必要的包,写了一个测试用例,测试通过,很好很强大。 没有 Nodejs 环境可以使用 Jasmine 做单元测试吗?当然可以,我们可以到 官网下一个示例看一看,比较简单。今天先讲一下如果直接使用

    jasmine 做单元测试

       简单示例

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

           

      lib 文件夹下面:  boot.js 启动文件  ,

                console.js 输出辅助文件,

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

                  jasmine.js jasmine核心文件

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

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

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

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

       

    <!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>
    

        我们直接运行 SpecRunner.html  测试结果如下:

    5个 specs,0个失败,全部通过。在 PlayerSpec.js 里添加一个Suite,看看报错是什么样的。

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

     

    哈哈,测试未通过,看到没,这里显示了详细的错误信息。

       jasmine 语法详解

            首先了解几个概念: Suite 指一个测试集, describe方法标志着一个测试集。

                Spec 表示测试用例,jasmine中用方法it来开始 specs。

                一个 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');  只是调用了 Jasmine 保存的这个函数的 存根,不会影响到实际的值 ,如果没看明白请仔细看代码上我添加的注释。

    示例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();
        });
    }); 
    

     看完上面的示例应该在项目中应用没有什么问题了。

  • 相关阅读:
    kafka学习-卡不卡的简单使用
    mongoDB-mongo compass使用学习-最像关系型数据库的非关系型文档型数据库
    postman使用-跑男常用功能
    jmeter数据驱动csv+批量导出数据到csv文件
    jmeter-数据库查询与调用-mysql
    arch linux mysql using
    Win10安装 oracle11g 出现INS-13001环境不满足最低要求解决方法
    oracle11g数据库安装
    安装PLSQLDeveloper
    oracle基本学习
  • 原文地址:https://www.cnblogs.com/fengh/p/6121846.html
Copyright © 2011-2022 走看看