gpt4 book ai didi

javascript - 如何监视 JavaScript 中的递归函数

转载 作者:行者123 更新时间:2023-11-28 20:18:05 25 4
gpt4 key购买 nike

注意:我见过以不同方式和引用不同测试工具提出的这个问题的变体。我认为清楚地描述问题和解决方案会很有用。我的测试是使用 Sinon spies 编写的为了便于阅读,将使用 Jest 运行或 Jasmine (使用 Mocha 和 Chai 只需进行微小的更改即可运行),但是使用任何测试框架和任何 spy 实现都可以看到所描述的行为。

问题

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

示例

给定这个递归函数:

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

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

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

最佳答案

问题

spy 通过围绕跟踪调用和返回值的原始函数创建包装函数来工作。 spy 只能记录通过它的调用。

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

解决方案

递归函数调用自身的方式必须与从外部调用它的方式相同。然后,当函数被包裹在 spy 中时,递归调用被包裹在同一个 spy 中。

示例 1:类方法

递归类方法使用 this 调用它们自己,它引用它们的类实例。当实例方法被 spy 替换时,递归调用自动调用同一个 spy :

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

注意:类方法使用 this 所以为了调用使用 spy(10) 的 spy 函数; 而不是 instance.fibonacci(10); 该函数要么需要转换为箭头函数,要么使用 this.fibonacci = this.fibonacci 显式绑定(bind)到实例.bind(this); 在类构造函数中。

示例 2:模块

如果模块中的递归函数使用模块调用自身,则该递归函数可以被监视。当模块函数被 spy 替换时,递归调用会自动调用同一个 spy :

ES6

// ---- lib.js ----
import * as lib from './lib';

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


// ---- lib.test.js ----
import * as sinon from 'sinon';
import * as lib from './lib';

describe('fibonacci', () => {
it('should calculate Fibonacci numbers', () => {
expect(lib.fibonacci(5)).toBe(5);
expect(lib.fibonacci(10)).toBe(55);
});
it('should call itself recursively', () => {
const spy = sinon.spy(lib, 'fibonacci');
spy(10);
expect(spy.callCount).toBe(177); // PASSES
spy.restore();
});
});

Common.js

// ---- lib.js ----
exports.fibonacci = (n) => {
if (n < 0) throw new Error('must be 0 or greater');
if (n === 0) return 0;
if (n === 1) return 1;
// call fibonacci using exports
return exports.fibonacci(n - 1) + exports.fibonacci(n - 2);
}


// ---- lib.test.js ----
const sinon = require('sinon');
const lib = require('./lib');

describe('fibonacci', () => {
it('should calculate Fibonacci numbers', () => {
expect(lib.fibonacci(5)).toBe(5);
expect(lib.fibonacci(10)).toBe(55);
});
it('should call itself recursively', () => {
const spy = sinon.spy(lib, 'fibonacci');
spy(10);
expect(spy.callCount).toBe(177); // PASSES
spy.restore();
});
});

示例 3:对象包装器

如果将不属于模块的独立递归函数放置在包装对象中并使用该对象调用自身,则它可以成为可监视的。当对象中的函数被 spy 替换时,递归调用会自动调用同一个 spy :

const wrapper = {
fibonacci: (n) => {
if (n < 0) throw new Error('must be 0 or greater');
if (n === 0) return 0;
if (n === 1) return 1;
// call fibonacci using the wrapper
return wrapper.fibonacci(n - 1) + wrapper.fibonacci(n - 2);
}
};

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

关于javascript - 如何监视 JavaScript 中的递归函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51699584/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com