网站搜索

javascript - Jest - 在 TypeScript 中模拟一个命名类导出


我有一个 Node 模块,它导出几个类,其中一个是 Client,我用它来创建一个客户端(具有一些作为方法的 API)。

我正在尝试使用 Jest 测试我的模块,该模块使用这个节点模块作为依赖。然而,我一直无法成功模拟 Client 类中的一个方法(比如 search())。

这是我对myModule的规格说明:

//index.spec.ts
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
  beforeAll(() => {
    nock.disableNetConnect();
  });
  it('test search method in my module', () => {
    jest.mock('node-module-name');

    const mockedClient = <jest.Mock<externalModule.Client>>externalModule.Client;

    const myClient = createClient({/*params*/}); //returns instance of Client class present in node module by executing Client() constructor
    myClient.searchByName('abc'); //calls search API - I need to track calls to this API

    expect(mockedClient).toHaveBeenCalled();
    expect(mockedClient.prototype.search).toHaveBeenCalledWith('abc');
  });
});

然而,这根本不会创建模拟,并会触发 nock 错误,因为搜索 API 会尝试连接到通过参数提供的 URL。

我也尝试过像下面这样模拟 Client 类。虽然成功为 Client 类以及搜索 API 创建了一个模拟(通过控制台日志验证 search() 也被模拟了),但当我尝试检查 search() 是否被调用时,它会报错。

externalModule.Client = jest.fn(() => { return { search: jest.fn(() => Promise.resolve('some response')) } });
//creates the mock successfully, but not sure how to track calls to 'search' property

const client = myModule.createClient(/*params*/);
client.searchByName('abc');

expect(externalModule.Client).toHaveBeenCalled(); //Successful
expect(externalModule.Client.prototype.search).toHaveBeenCalled(); //returns error saying "jest.fn() value must be a mock function or spy, Received: undefined"

我不确定我做错了什么。提前谢谢你。

答案#1

模拟整个模块

尝试将 jest.mock 移动到文件顶部

//index.spec.ts
const search = jest.fn();
jest.mock('node-module-name', () => ({
  Client: jest.fn(() => ({ search }))
}));
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
  beforeAll(() => {
    nock.disableNetConnect();
  });
  it('test search method in my module', () => {
    const myClient = createClient({/*params*/});
    myClient.searchByName('abc'); 

    expect(externalModule.Client).toHaveBeenCalled();
    expect(search).toHaveBeenCalledWith('abc');
    externalModule.Client.mockClear();
    search.mockClear();
  });
});

仅模拟客户端

创建 search 常量并跟踪它。

const search = jest.fn();
externalModule.Client = jest.fn(() => ({ search }));

const client = myModule.createClient(/*params*/);
client.searchByName('abc');

expect(externalModule.Client).toHaveBeenCalled();
expect(search).toHaveBeenCalled();

答案#2

这是我如何模拟它的。我不得不更改命名并删除一些代码,以避免暴露原始源代码。

jest.mock('../foo-client', () => {
  return { FooClient: () => ({ post: mockPost }) }
})

完整代码。

// foo-client.ts

export class FooClient {
    constructor(private config: any)
    post() {}
}

// foo-service.ts

import { FooClient } from './foo-client'

export class FooLabelService {
    private client: FooClient
    constructor() {
        this.client = new FooClient()
    }

    createPost() {
        return this.client.post()
    }
}

// foo-service-test.ts

import { FooService } from '../foo-service'

const mockPost = jest.fn()
jest.mock('../foo-client', () => {
  return { FooClient: () => ({ post: mockPost }) }
})

describe('FooService', () => {
  let fooService: FooService

  beforeEach(() => {
    jest.resetAllMocks()
    fooService = new FooService()
  })

  it('something should happened', () => {
    mockPost.mockResolvedValue()
    fooService.createPost()
  })
})

相关文章