Jasmine文檔(二)

前言

這是翻譯的Jasmine文檔第二篇,第一篇文章的地址爲:
Jasmine文檔(一)

Spies(間諜)

Jasmine有稱爲間諜(spies)的測試雙重功能。一個spy可以監測任何函數的調用和參數的調用痕跡。Spy只能存在於定義它的describe()it()代碼塊內,而在每一個spec結束後將被移除。(這個語法在Jasmine2.0才改變的)
有幾個特別的Matchers於spy相互作用:
toHaveBeenCalled():在spy被調用是返回true;
toHaveBeenCalledTimes():在spy調用指定次數的時候會通過測試;
toHaveBeenCalledWith():如果匹配任一調用的參數列表,則返回true。
例子:

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

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

    spyOn(foo, 'setBar');//使用spyOn()來聲明spy

    foo.setBar(123);
    foo.setBar(456, 'another param');
  });
  it("tracks that the spy was called", function() {
    //測試spy是否調用
    expect(foo.setBar).toHaveBeenCalled();
  });
  it("tracks that the spy was called x times", function() {
    //測試spy是否調用兩次
    expect(foo.setBar).toHaveBeenCalledTimes(2);
  });
  it("tracks all the arguments of its calls", function() {
    //測試spy調用時的參數列表是否匹配
    expect(foo.setBar).toHaveBeenCalledWith(123);
    expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
  });

  it("stops all execution on a function", function() {
    //Spy的調用並不會影響真實的值,所以bar仍然是null。
    expect(bar).toBeNull();
  });
});

and.callThrough

Spy通過鏈式調用and.callThrough,除了追蹤所有的調用之外,它將委託實際的實現值。例如:

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();//定義spy並且鏈式調用and.callThrough()

    foo.setBar(123);
    fetchedBar = foo.getBar();//調用spy
  });

  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(123);//fetchedBar爲函數實際返回的值。
  });
});

將上述代碼對比於一下代碼:

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() {
    expect(fetchedBar).toBeUndefined();//此時的fetchedBar不再是函數返回的實際值,而是undefined
  });
});

and.returnValue

Spy通過鏈式調用and.returnValue,所有調用spy的都將返回一個指定值。例如:

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);//指定返回745

    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(745);//返回特定值爲745
  });
});

and.returnValues

Spy通過鏈式調用and.returnValues,所有調用該spy的函數都將按順序返回一些特定的值,直到返回值隊列的最後,這之後的所有調用將返回undefined。例如:

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() {
    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();//之後的調用都將返回undefined
  });
});

and.callFake

Spy通過調用and.callFake,所有調用該spy的都將調用其提供的函數,例如:

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

and.throwError

Spy鏈式調用and.throwError,調用該spy的將以一個錯誤的形式拋出特殊返回值,例如:

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");
  });
});

and.stub

Spy鏈式調用以上某一個策略後,可以調用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);//調用策略and.callThrough()所定義的spy
    expect(bar).toEqual(123);//bar數值被修改爲123

    foo.setBar.and.stub();//調用and.stub()
    bar = null;

    foo.setBar(123);//調用spy
    expect(bar).toBe(null);//bar不再返回and.callThrough()的實現值
  });
});

其他跟蹤屬性

任何調用spy的都將被追蹤,並且暴露在calls的屬性中。
calls屬性有:
1. .calls.any():一次都沒調用時返回false,一旦調用至少一次就返回true;
2. .calls.count():返回spy調用的次數
3. .calls.argsFor(index):返回第index+1次調用時傳遞的參數,index從0開始;
4. .calls.allArgs():以數組的形式返回調用時傳遞的所有參數;
5. .calls.all():以對象形式返回上下文(this),以及所有傳遞的參數;
6. .calls.mostRecent():以對象形式返回最近一次調用的上下文(this),以及傳遞的參數;
7. .calls.first():以對象形式返回第一次調用的上下文(this),以及傳遞的參數;(當檢查.calls.all().calls.mostRecent().calls.first()返回的對象時,.object屬性指向的是調用該spy的this對象)
8. .calls.reset():清空spy的所有追蹤。
上述屬性值,例子如下:

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

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

    spyOn(foo, 'setBar');
  });
    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);
  });
    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);
  });
    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"]);
  });
    it("tracks the arguments of all calls", function() {
    foo.setBar(123);
    foo.setBar(456, "baz");

    expect(foo.setBar.calls.allArgs()).toEqual([[123],[456, "baz"]]);
  });
    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}]);
  });
    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});
  });
    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});
  });
    it("tracks the context", function() {
    var spy = jasmine.createSpy('spy');
    var baz = {
      fn: spy
    };
    var quux = {
      fn: spy
    };
    baz.fn(123);
    quux.fn(456);

    //當檢查.calls.all(),.calls.mostRecent(),.calls.first()返回的對象時,.object屬性指向的是調用該spy的this對象
    expect(spy.calls.first().object).toBe(baz);
    expect(spy.calls.mostRecent().object).toBe(quux);
  });
    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);
  });
});

Spies:createSpy

如果沒有一個函數可以spyOn,jasmine.createSpy可以創建一個“空白”的spy。該spy會像其他間諜一樣追蹤調用,函數等等,但是在其之後並不會有實際實現的返回值。Spies是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");
  });
});

Spies:createSpyObj

爲了創建一個多重spies的模擬,使用jasmine.createSpyObj()並傳遞一個字符串的數組,將會返回一個對象,對象包括每個字符串綁定的spy屬性。例如:

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文檔(三)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章