前端測試框架 jasmine 的使用

来源:http://www.cnblogs.com/fengh/archive/2016/12/01/6121846.html
-Advertisement-
Play Games

最近的項目在使用AngulaJs,對JS代碼的測試問題就擺在了面前。通過對比我們選擇了 Karma + jasmine ,使用 Jasmine做單元測試 ,Karma 自動化完成,當然瞭如果使用 Karma + jasmine 前提是必須安裝 Nodejs。 安裝好 Nodejs ,使用 npm 安 ...


       最近的項目在使用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();
    });
}); 

 看完上面的示例應該在項目中應用沒有什麼問題了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文地址 原文地址 對於大型網站技術的理解,可以從架構技術原理的組織方式以架構要素作為維度,從系統性能、可用性、伸縮性、擴展性、安全性幾個角度闡述網站架構的技術要點。還有另一種較為直觀的組織方式,是從不同架構層次所使用的網站架構技術這個維度進行描述的。 網站系統架構層次如下圖所示: 這個網站架構層次 ...
  • 很實用的一款插件jQuery瀑布流從不同方向載入3種效果演示線上預覽 下載地址 實例代碼 <section class="grid-wrap"> <ul class="grid swipe-right" id="grid"> <li> <a href="#"><img src="/api/jq/57 ...
  • 一、HTML5新增屬性 1.1、contextmenu contextmenu的作用是指定右鍵菜單。 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="div1" ...
  • 本文介紹了css3中的box-sizing屬性,在這之前讀者需要預備知識width的範圍。 瀏覽器的支持情況 box-sizing屬性 box-sizing屬性可以有三個值:content-box(defalut),border-box屬性。 <style> div{ width:100px; he ...
  • 1)在折線圖中,有時我們不想讓太多折線顯示,那麼就隱藏,點擊legend區域文字再顯示。 比如我們要隱藏的折線叫”聯盟廣告“,代碼如下 在series中依然有它相關的數據 這樣,當我們點擊的時候,折線就顯示了。 2)折線坐標軸太粗,不夠細?顏色不好看? 那麼,可以這樣改 y軸也同理。 3)分隔線顏色 ...
  • 目錄 一、HTML5概要 1.1、為什麼需要HTML5 1.2、什麼是HTML5 1.3、HTML5現狀及瀏覽器支持 1.4、HTML5特性 1.5、HTML5優點與缺點 1.5.1、優點 1.5.2、缺點 1.6、HTML5效果展示 1.7、HTML5學習與開發工具 1.7.1、基礎要求 1.7. ...
  • 因為項目需要,要求實現類似力導圖效果的圖,我就瞄上了echarts。 註意事項1:由於我的項目要部署到內網,所以js文件要在本地,網上大多力導圖都是echarts2的,而其又依賴zrender基礎庫,下載的echarts2是2.2.7版本,但是去zrender官網下載的2.1版本,用起來說版本最低要 ...
  • 前言 更好的閱讀體驗請:我的微信小程式入門踩坑之旅 小程式出來也有一段日子了,剛出來時也留意了一下。不過趕上生病,加上公司里也有別的事,主要是自己犯懶,就一直沒做。這星期一,趕緊趁著這股熱乎勁,也不是很忙,終於磨磨唧唧的寫了個小demo,(當然還有好多介面沒有使用)。 預計閱讀時間:5min 正文 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...