如何在类中用笑话来模拟节点模块?

时间:2019-09-02 13:34:36

标签: javascript typescript unit-testing jestjs

我需要在一个类中模拟DNS节点模块,但是由于该类包含在类中,因此我不确定该如何做。这是该类的示例...

import { lookup } from 'dns';

class Foo {
    // ...
    protected async _bar(IP: string) {
        // I want to mock "lookup"
        await new Promise<undefined>((resolve, reject) => {
            lookup(IP, (err, addr) => {
                if (err) reject(new Error('DNS Lookup failed for IP_ADDR ' + IP));

                resolve();
            });
        });

        // If dns found then return true
        return true;
    }
    // ...
}

我想创建一个包含类似于以下内容的测试的测试文件foo.spec.ts

import { Foo } from './Foo';

describe('Foo', () => {
    it('Bar Method returns true on success', () => {
        const test = new Foo();

        expect(test._bar('192.168.1.1')).resolves.toBeTruthy();
    });
});

由于类定义与测试本身位于单独的文件中,因此我不确定如何在类lookup中模拟Foo调用。

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:1)

来自玩笑的tutorial

jest.mock('./sound-player', () => {
  return function() {
    return {playSoundFile: () => {}};
  };
});

所以您可以像jest.mock('dns', () => { ... });

答案 1 :(得分:1)

您使用lookup的方式将不起作用,因为它不会返回Promise ...

...但是您可以使用util.promisify将其转换为确实返回Promise的版本。

代码最终看起来像这样:

import { lookup as originalLookup } from 'dns';  // <= import original lookup...
import { promisify } from 'util';
const lookup = promisify(originalLookup);  // <= ...and promisify it

export class Foo {
  async _bar(IP: string) {
    await lookup(IP).catch(err => { throw new Error('Failed'); });
    return true;
  }
}

然后您可以像下面这样使用lookup在测试中模拟jest.mock

import { Foo } from './Foo';

jest.mock('dns', () => ({
  lookup: (hostname, callback) => {
    hostname === 'example.com' ? callback() : callback('error');
  }
}))

describe('Foo', () => {
  it('Bar Method returns true on success', async () => {
    const test = new Foo();
    await expect(test._bar('example.com')).resolves.toBeTruthy();  // Success!
    await expect(test._bar('something else')).rejects.toThrowError('Failed');  // Success!
  });
});

请注意,由于需要提升并运行jest.mock的调用,因此需要使用jest.spyOn(而不是jest.mock之类的东西来创建模拟)。导入Foo.js之前,该模拟必须就位,因为它的第一件事是创建并存储承诺的lookup