【问题标题】:How to Inject Global into typescript for testing?如何将全局注入打字稿进行测试?
【发布时间】:2018-12-20 04:44:06
【问题描述】:

我有一个打字稿模块,它使用浏览器的原生 Blob。我想在节点中测试它,所以我需要同一个模块来查看全局假 Blob 实现。一个非常简单的假 blob 实现对我有用

class Blob {

  parts?: any;
  options?: any;

  constructor(parts: any, options?: any) {
    this.parts = parts;
    this.options = options;
  }

  getType():string {
    return options.type;  // I know, hacky by just a demo
  }
}

但是如何将它注入节点的全局命名空间,以便我在浏览器中运行测试时通常在浏览器中运行的代码可以看到它?

换句话说,假设我有一个预计会返回本机浏览器Blob的类

export class BlobMaker {
  static makeTextBlob(text):Blob {
    return new Blob([text], {type: 'text/text'});
  }
}

现在我想在节点(而不是浏览器)中测试它

import { BlobMaker } from '../src/blob-maker';
import * as expect from 'expect';

describe('BlobMaker', () =>  {
  it('makes a text blob', () => {
    const blob = BlobMaker.makeTextBlob('foo');

    expect(blob.getType()).equalTo('text/text');
  });
});

编译失败,因为节点中不存在Blob

显然我可以通过添加来声明它存在?

export interface Global extends NodeJS.Global {
  Blob: Blob;
}

但我仍然需要在上面注入我的假 Blob 类,以便我正在测试的代码将使用它。有没有办法做到这一点,或者我应该以其他方式解决这个问题?

我似乎无法将 Blob 抽象为某种接口,因为我需要 makeTextBlob 的签名才能成为真正的原生浏览器 Blob 而不是某种自定义类型。

我想我知道我可以通过测试将 Blob 工厂深入到我的库中。经过这么深的 Blob 工厂似乎有点矫枉过正。我实际上是通过打字稿抱怨的。我认为是因为它认为BlobMaker.makeBlob 返回的类型不同

这是代码

let makeBlob = function(...args):Blob {
   return new Blob(...args);
};

export function setMakeBlob(fn):void {
   makeBlob = fn;
};

export class BlobMaker {
  static makeTextBlob(text):Blob {
    return makeBlob([text], {type: 'text/text'});
  }
}

然后在测试中

import { BlobMaker, setMakeBlob } from '../src/blob-maker';
import { Blob } from "./fake-blob';
import * as expect from 'expect';

describe('BlobMaker', () =>  {
  it('makes a text blob', () => {
    setMakeBlob(function(parts, options) {
      return new Blob();
    });

    const blob = BlobMaker.makeTextBlob('foo');

    expect(blob.getType()).equalTo('text/text');   // ERROR!
  });
});

我收到一个错误,Blob 上没有 getType 方法。我猜 TS 认为 BlobMaker.makeTextBlob 返回的 Blob 是原生的。我试着投射它

    const blob = BlobMaker.makeTextBlob('foo') as Blob;

但它也不喜欢那样。

如果我可以将我的 Blob 类注入到 node.js 中的全局命名空间中,这似乎都可以解决。那么,我该怎么做呢?

【问题讨论】:

  • 我的印象是 /// <referenceing 来自 BlobMaker 模块的 .d.ts 文件就足够了,你试过了吗?
  • 抱歉,这是我的第一次打字体验,但这将如何帮助 BlobMaker 找到 Blob
  • 哦等等我看错了,你的Blobclass,而不是interface(这应该意味着它在一个普通的.ts 文件中。你的@ 987654352@ 模块应该是import Blob from './blob' 或任何导出它的地方。即使它被理解为一个全局变量,但在使用 TypeScript 时将它作为模块的一部分处理要容易得多。
  • 我不确定这有什么帮助。此代码仍然需要在浏览器中运行并返回浏览器原生Blob(更新问题)。我只想在节点中运行的测试中使用假 blob 对其进行测试。
  • 我自己还不是特别精通 TypeScript,但是看看这里的上下文,我认为您应该考虑模拟 Blob 模块。我发现 this answer 可能对你有用也可能没用,但你可以做的是重写 Blob 所在的文件以附加到 global,前提是 global.Blob 上没有定义任何内容。

标签: typescript


【解决方案1】:

所以这就是我解决它的方法。希望不要太可怕

首先根据this Q&A我可以通过添加一个单独的文件来扩展node中的全局对象。就我而言,我制作了 test.d.ts 并将其放入其中

declare namespace NodeJS {
    interface Global {
        Blob: any
    }
}

然后在我的测试中我这样做了

import { BlobMaker } from '../src/blob-maker';
import * as expect from 'expect';

class Blob {

  parts?: any;
  options?: any;

  constructor(parts: any, options?: any) {
    this.parts = parts;
    this.options = options;
  }

  getType():string {
    return options.type;  // I know, hacky by just a demo
  }
}

describe('BlobMaker', () =>  {
  it('makes a text blob', () => {

    global.Blob = Blob;

    const blob = BlobMaker.makeTextBlob('foo');

    expect(blob.getType()).equalTo('text/text');
  });
});

它正在工作。我可能应该将修补 global.Blob 的部分移到另一个模块,但它至少提出了一个解决方案。

【讨论】:

  • 这行得通,但只是为了确保它不会潜在地干扰其他测试,我会通过重写 describe('BlobMaker', () => { const { Blob } = global; beforeEach(() => { global.Blob = require('./blob'); }); afterEach(() => { global.Blob = Blob; }); it(...); }); 来更明确地说明模拟
猜你喜欢
  • 2017-10-20
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 2012-11-28
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多