具有相同命名空间名称时的模拟库函数

Posted

技术标签:

【中文标题】具有相同命名空间名称时的模拟库函数【英文标题】:Mocking library function when it has the same namespace name 【发布时间】:2021-12-23 06:06:18 【问题描述】:

类似于previous question,我正在尝试使用sinon 模拟一个外部库。但是,该库使用相同的名称 FastGlob 导出两个函数和一个命名空间。

我对函数重载有基本的了解,但我不确定命名空间如何与函数重载一起工作,或者这个问题是否相关。

无论如何,我想模拟第一个函数定义,但 sinon 看到的是命名空间

declare function FastGlob(source: PatternInternal | PatternInternal[], options: OptionsInternal & EntryObjectPredicate): Promise<EntryInternal[]>;

这是库定义文件

import  Options as OptionsInternal  from './settings';
import  Entry as EntryInternal, FileSystemAdapter as FileSystemAdapterInternal, Pattern as PatternInternal  from './types';

declare function FastGlob(source: PatternInternal | PatternInternal[], options: OptionsInternal & EntryObjectPredicate): Promise<EntryInternal[]>;
declare function FastGlob(source: PatternInternal | PatternInternal[], options?: OptionsInternal): Promise<string[]>;
declare namespace FastGlob 
    type Options = OptionsInternal;
    type Entry = EntryInternal;
    type Task = taskManager.Task;
    type Pattern = PatternInternal;
    type FileSystemAdapter = FileSystemAdapterInternal;
    function sync(source: PatternInternal | PatternInternal[], options: OptionsInternal & EntryObjectPredicate): EntryInternal[];
    function sync(source: PatternInternal | PatternInternal[], options?: OptionsInternal): string[];
    function stream(source: PatternInternal | PatternInternal[], options?: OptionsInternal): NodeJS.ReadableStream;
    function generateTasks(source: PatternInternal | PatternInternal[], options?: OptionsInternal): Task[];
    function isDynamicPattern(source: PatternInternal, options?: OptionsInternal): boolean;
    function escapePath(source: PatternInternal): PatternInternal;

export = FastGlob;

我尝试过使用以下测试的变体,但 TS 抱怨它只能找到命名空间内的函数(同步、流等)。删除函数的字符串名称会导致不同的问题。

import * as FastGlob from 'fast-glob';
import  stub, SinonStub  from "sinon";
import  Pattern, Entry, Options  from "fast-glob";

(stub(FastGlob, "FastGlob") as unknown as SinonStub<[s: Pattern | Pattern[], o: Options], Promise<Entry[]>>).resolves([test: '/test/' as unknown as Entry])

应用程序代码是这样使用的

import * as glob from 'fast-glob';
const paths: Array<string> = await glob('./my/glob/**/*.ts',  absolute: true );

【问题讨论】:

【参考方案1】:

您需要额外的模块来存根 fast-glob,因为它定义的方式。欲了解更多信息,您可以查看此sinon issue。

如果您可以使用附加模块,我可以举个例子:proxyquire。

我有这个 glob.ts。

// File: glob.ts
import glob from 'fast-glob';


async function getPaths(input: string): Promise<Array<glob.Entry|string>> 
  return glob(input,  absolute: true );


export  getPaths ;

使用规范文件进行测试:

// File: glob.spec.ts
import * as FastGlob from 'fast-glob';
import sinon from 'sinon';
import proxyquire from 'proxyquire';
import  expect  from 'chai';

describe('Glob', () => 
  const fakeInput = './node_modules/**/settings.js';
  it('getPaths using first fast-glob definition', async () => 
    const fakeResult = [ test: '/test/'  as unknown as FastGlob.Entry];
    const fakeFunc = sinon.fake.resolves(fakeResult);
    // Create stub using proxyquire.
    const glob = proxyquire('./glob', 
      'fast-glob': sinon.fake.resolves(fakeResult),
    );
    const paths = await glob.getPaths(fakeInput);
    expect(paths).to.deep.equal(fakeResult);
    expect(fakeFunc.calledOnceWithExactly(fakeInput));
  )

  it('getPaths using second fast-glob definition', async () => 
    const fakeResult = ['/test/'];
    const fakeFunc = sinon.fake.resolves(fakeResult);
    // Create stub using proxyquire.
    const glob = proxyquire('./glob', 
      'fast-glob': sinon.fake.resolves(fakeResult),
    );
    const paths = await glob.getPaths(fakeInput);
    expect(paths).to.deep.equal(fakeResult);
    expect(fakeFunc.calledOnceWithExactly(fakeInput));
  )
);

当你从终端使用 ts-mocha 和 nyc 运行它时:

$ npx nyc ts-mocha glob.spec.ts 


  Glob
    ✔ getPaths using first fast-glob definition (137ms)
    ✔ getPaths using second fast-glob definition


  2 passing (148ms)

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

【讨论】:

你是对的,但最后我找到了一个现代的 npm glob 库,开箱即用

以上是关于具有相同命名空间名称时的模拟库函数的主要内容,如果未能解决你的问题,请参考以下文章

[C++]——函数的重载

cpp►命名空间namespace

来自不同命名空间的同名对象的可重用函数代码?

使用具有相同 ClassName 的其他命名空间扩展类

Rails 和 RSpec:在不同的命名空间(模块)中测试具有相同名称的控制器

C 语言的命名空间