当两个方法名称相同但参数不同时使用 sinon.spy

using sinon.spy when two methods have the same name but different arguments

我正在尝试在一个对象上使用 sinon.spy(),该对象有两个名为 draw 的方法(名称相同但参数不同),如下所示:

const spy = sinon.spy(obj,'draw')

当我尝试使用 expect(spy.calledOnceWith(expectedArgument)).toBeTruthy() 测试间谍时,其中 expectedArgument 与 draw 的函数定义之一给出的类型匹配,我收到一条错误消息,指出该参数不可分配给指定的类型draw 的另一个函数声明。在这种情况下,有没有一种方法可以指定 header/declaration 我在创建间谍时指的是哪个函数,以便参数匹配?

YES,您需要对 sinon 间谍进行双重断言以避免 typescript lint 错误。这个 lint 错误是因为 sinon spy 自动应用已知的最后一个定义。您需要提供将在测试中使用的正确断言。

例如:我在文件add.ts中添加了class。如果两个输入都是数字,那么 process 方法将尝试将它们相加。如果两个输入都是字符串,那么 process 方法将尝试连接字符串。其他的会产生TypeError.

class Add {
  process(a: number, b: number): number;
  process(a: string, b: string): string;
  process(a: any, b: any):number|string|Error {
    if (typeof a === 'number' && typeof b === 'number') {
      return a + b;
    } else if (typeof a === 'string' && typeof b === 'string') {
      return `${a}${b}`;
    }
    throw new TypeError('Incorrect type to process');
  }
}

export default Add;

我有测试文件:add.spec.ts 将测试所有条件。

import sinon from 'sinon';
import { expect } from 'chai';
import Add from '../src/add';

describe('Add', () => {
  afterEach(() => {
    sinon.restore();
  });
  it('should process number', () => {
    const obj = new Add();
    const a = 1;
    const b = 2;
    // Sinon spy automatically try to apply the last definition known, which is:
    // sinon.SinonSpy<[a: string, b: string], string>
    // So we need to do type assertions to:
    // sinon.SinonSpy<[a: number, b: number], number>
    const spy = sinon.spy(obj, 'process') as unknown as sinon.SinonSpy<[a: number, b: number], number>;
    const result = obj.process(a, b);
    expect(result).to.equal(a + b);
    // This line: linter will not complain.
    expect(spy.calledOnceWith(a, b)).to.equal(true);
  });

  it('should process string', () => {
    const obj = new Add();
    const a = '1';
    const b = '2';
    // Sinon spy has automatically apply the correct definition.
    const spy = sinon.spy(obj, 'process'); // as unknown as sinon.SinonSpy<[a: string, b: string], string>;
    const result = obj.process(a, b);
    expect(result).to.equal(`${a}${b}`);
    // This line: linter will not complain.
    expect(spy.calledOnceWith(a, b)).to.equal(true);
  });

  it('should throw error for invalid type', () => {
    const obj = new Add();
    const a = 1;
    const b = '2';
    // Sinon spy automatically try to apply the last definition known, which is:
    // sinon.SinonSpy<[a: string, b: string], string>
    // So we need to do type assertions to:
    // sinon.SinonSpy<[a: any, b: any], Error>
    const spy = sinon.spy(obj, 'process') as unknown as sinon.SinonSpy<[a: any, b: any], Error>;
    // We also need to cast obj as any, or there will be lint error:
    // The call would have succeeded against this implementation,
    // but implementation signatures of overloads are not externally visible.
    expect(() => (obj as any).process(a, b)).to.throw(TypeError);
    // This line: linter will not complain.
    expect(spy.calledOnceWith(a, b)).to.equal(true);
  });
});

当我在终端使用 ts-mocha 调用 runner 并使用 nyc 进行覆盖时,我得到:(我的编辑也没有抗议)。

$ npx nyc ts-mocha test/add.spec.ts 


  Add
    ✓ should process number
    ✓ should process string
    ✓ should throw error for invalid type


  3 passing (5ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 add.ts   |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------

参考:https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions