如何监视 JavaScript 中的递归函数

2024-04-18

Note:我已经看到这个问题以不同的方式提出并参考不同的测试工具。我认为清楚地描述问题和解决方案会很有用。我的测试是使用编写的诗乃间谍 https://sinonjs.org/为了可读性并将使用运行Jest https://jestjs.io/en/ or Jasmine https://jasmine.github.io/index.html(并且只需要进行微小的更改即可使用 Mocha 和 Chai 运行),但是使用任何测试框架和任何间谍实现都可以看到所描述的行为。

ISSUE

我可以创建测试来验证递归函数是否返回正确的值,但我无法监视递归调用。

EXAMPLE

给定这个递归函数:

const fibonacci = (n) => {
  if (n < 0) throw new Error('must be 0 or greater');
  if (n === 0) return 0;
  if (n === 1) return 1;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

...我可以通过这样做来测试它是否返回正确的值:

describe('fibonacci', () => {
  it('should calculate Fibonacci numbers', () => {
    expect(fibonacci(5)).toBe(5);
    expect(fibonacci(10)).toBe(55);
    expect(fibonacci(15)).toBe(610);
  });
});

...但是如果我向该函数添加一个间谍,它会报告该函数仅被调用一次:

describe('fibonacci', () => {
  it('should calculate Fibonacci numbers', () => {
    expect(fibonacci(5)).toBe(5);
    expect(fibonacci(10)).toBe(55);
    expect(fibonacci(15)).toBe(610);
  });
  it('should call itself recursively', () => {
    const spy = sinon.spy(fibonacci);
    spy(10);
    expect(spy.callCount).toBe(177); // FAILS: call count is 1
  });
});

ISSUE

间谍的工作方式是围绕原始函数创建一个包装函数来跟踪调用和返回值。间谍只能记录通过它的通话。

如果递归函数直接调用自身,则无法将该调用包装在间谍中。

SOLUTION

递归函数必须以与从外部调用自身的方式相同的方式调用自身。然后,当函数包装在间谍中时,递归调用将包装在同一个间谍中。

示例 1:类方法

递归类方法使用以下方式调用自身this这是指他们的类实例。当实例方法被间谍替换时,递归调用会自动调用相同的间谍:

class MyClass {
  fibonacci(n) {
    if (n < 0) throw new Error('must be 0 or greater');
    if (n === 0) return 0;
    if (n === 1) return 1;
    return this.fibonacci(n - 1) + this.fibonacci(n - 2);
  }
}

describe('fibonacci', () => {

  const instance = new MyClass();

  it('should calculate Fibonacci numbers', () => {
    expect(instance.fibonacci(5)).toBe(5);
    expect(instance.fibonacci(10)).toBe(55);
  });
  it('can be spied on', () => {
    const spy = sinon.spy(instance, 'fibonacci');
    instance.fibonacci(10);
    expect(spy.callCount).toBe(177); // PASSES
    spy.restore();
  });
});

Note:类方法使用this所以为了调用 sied 函数spy(10);代替instance.fibonacci(10);该函数需要转换为箭头函数或显式绑定到实例this.fibonacci = this.fibonacci.bind(this);在类构造函数中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何监视 JavaScript 中的递归函数 的相关文章