繁体   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