簡體   English   中英

單元測試時如何測試綁定函數的相等性?

[英]How can I test for equality to a bound function when unit testing?

我想測試傳遞給函數的參數是否是函數引用,但函數引用是使用bind()傳遞的。

考慮這個要測試的代碼(為簡潔起見縮短):

initialize: function () {
    this.register(this.handler.bind(this));
}

這個單元測試檢查register()是否被handler()調用:

it('register handler', function () {
    spyOn(bar, 'register');
    bar.initialize();
    expect(bar.register.calls.argsFor(0)[0]).toEqual(bar.handler);
});

由於使用bind()綁定函數,arg 不等於我猜測的函數引用 - 如何在仍然使用bind()方法的同時測試是否傳遞了正確的函數引用?

注意:這不是茉莉花特有的,我只是因為所使用的方法而認為它是合適的。

代替

expect(bar.register.calls.argsFor(0)[0]).toEqual(bar.handler);

你可以做

expect(Object.create(bar.handler.prototype) instanceof bar.register.calls.argsFor(0)[0])
  .toBe(true);

或者

expect(Object.create(bar.handler.prototype)).
  toEqual(jasmine.any(bar.register.calls.argsFor(0)[0]));

這是有效的,因為綁定函數的內部[[HasInstance]]方法委托給原始函數的[[HasInstance]]方法。

這篇博文對綁定函數進行了更詳細的分析。

this.handler.bind(this)創建了一個全新的函數,因此它不等於bar.handler 參見Function.prototype.bind()

您可以將有界函數作為參數傳遞給initialize函數,然后對其進行測試,例如:

var handler = bar.handler.bind(bar);
bar.initialize(handler);
expect(bar.register.calls.argsFor(0)[0]).toEqual(handler);

我設法保留了測試和代碼並解決了它。

我用一個空的匿名函數監視函數引用,然后在監視 register 方法時調用它 - 如果調用了 spy,我知道它傳遞了正確的引用。

it('register handler', function () {
    spyOn(bar, 'handler').and.callFake(function(){}); // do nothing
    spyOn(bar, 'register').and.callFake(function(fn){
        fn();
        expect(bar.handler).toHaveBeenCalled();
    });
    bar.initialize();
});

就我而言(使用jest ),我只是模擬了我想要的函數的 bind 實現,並對其進行了調整,使其返回原始函數而不是它的綁定副本。

具體來說,這是我嘗試和工作的:

待測試代碼:

// module test.js

export const funcsToExecute = [];
function foo(func) {
    funcsToExecute.push(func);
}

export function bar(someArg) {
    // bar body
}

export function run(someArg) {
    foo(bar.bind(null, someArg));
}

我想斷言在調用run時, funcsToExecute包含bar

所以我寫了這樣的測試:

import * as test from 'test';

it('should check that "funcsToExecute" contain only "bar"', () => {
    jest.spyOn(test.bar, 'bind').mockImplementation((thisVal, ...args) => test.bar);

    test.run(5);

    expect(test.funcsToExecute.length).toBe(1);
    expect(test.funcsToExecute[0]).toBe(test.bar);
});

對於你的例子,我想它會是這樣的:

it('register handler', function () {
    spyOn(bar, 'register');
    spyOn(bar.handler, 'bind').mockImplementation((thisVal, ...args) => bar.handler);
    bar.initialize();
    expect(bar.register.calls.argsFor(0)[0]).toBe(bar.handler);
});

雖然我沒有測試過。

我想我會添加另一種對我來說不那么尷尬的方法。

給定一個類,如:

class Bar {
  public initialize() {
    this.register(this.handler.bind(this));
  }
  private register(callback) {}
  private handler() {}
}

完整的規范可能如下所示:

describe('Bar', () => {
  let bar;

  beforeEach(() => {
    bar = new Bar();
  });

  describe('initialize', () => {
    let handlerContext;

    beforeEach(() => {
      bar.handler = function() {
        handlerContext = this;
      };
      bar.register = jest.fn(callback => {
        callback();
      });
      bar.initialize();
    });

    it('calls register with the handler', () => {
      expect(bar.register).toHaveBeenCalledWith(expect.any(Function));
    });

    it('handler is context bound', () => {
      expect(handlerContext).toEqual(bar);
    });
  });
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM