如何清除 Jest 模拟实现以进行下一次测试?

Posted

技术标签:

【中文标题】如何清除 Jest 模拟实现以进行下一次测试?【英文标题】:How to clear Jest mock implementation for next tests? 【发布时间】:2019-12-20 18:37:50 【问题描述】:

我正在设置 Jest 来测试一个打字稿应用程序。

如何清除模拟函数并恢复其他测试的原始实现?

模拟我使用过的函数:jest.fn().mockImplementationOnce()

到目前为止,我已经在 beforeEach 和 afterEach 中尝试了 jest.clearAll() / resetModules() / resetAllMocks() ,但没有任何成功。

app.test.ts

import App from './app';
import  DbService  from './lib/dbService';

describe('App', () =>     
  let dbService: DbService;
  let app: App;

  beforeEach(() => 
    jest.clearAllMocks();
    dbService = new DbService();
    app = new App();
  );

  describe('getUsers', () => 

    it('Should get an array users #1', () => 
      expect(app).toBeInstanceOf(App);
      const allUsers = app.getAllUsers();
      expect(allUsers[0].id).toBeDefined();
    );

    it('should return an error #2', () => 
      DbService.prototype.getAllUsers =         
        jest.fn().mockImplementationOnce(() => 
            return new Error('No connection to DB');
         );
      expect(app.getAllUsers()).toEqual(new Error('No connection to DB'));
    );

    it('Should get an array users #3', () => 
       expect(app).toBeInstanceOf(App);
       const allUsers = app.getAllUsers();
       expect(allUsers[0].id).toBeDefined();
    ); 
  );
);

app.ts

import  DbService  from './lib/dbService';

export default class App 
  private dbService: DbService;

  constructor() 
    this.dbService = new DbService();
  

  getAllUsers() 
    return this.dbService.getAllUsers();
  

lib/dbService.ts

let instance: DbService;

export class DbService 
  constructor() 
    if (!instance) 
      instance = this;
    
    return instance;
  

  getAllUsers() 
   return [
     id: 1, username: 'john',
     id: 2, username: 'bill'
    ]
   

我希望测试 #3 像测试 #1 一样通过,但它实际上失败并出现以下错误:

FAIL  src/app.test.ts
  App
   getUsers
    √ Should get an array users #1 (3ms)
    √ should return an error #2 (1ms)
    × Should get an array users #3 (1ms)

● App › getUsers › Should get an array users #3

  TypeError: Cannot read property '0' of undefined

    31 |        expect(app).toBeInstanceOf(App);
    32 |        const allUsers = app.getAllUsers();
  > 33 |        expect(allUsers[0].id).toBeDefined();
       |                       ^
    34 |     );
    35 |   );
    36 | );

【问题讨论】:

【参考方案1】:

我不确定这是否是jest 实现此目的的方式,但我认为您可以将原始方法实现保存在一个变量中,并在每次测试后重新设置该方法,以防它在测试中被模拟。

例如

describe('App', () =>     
  let dbService: DbService;
  let app: App;
  let originalGetAllUsersFn = DbService.prototype.getAllUsers;

  //...

  afterEach(() => 
    // restore mocked method
    DbService.prototype.getAllUsers = originalGetAllUsersFn;
  );

);

【讨论】:

【参考方案2】:

Jest 具有设置/拆卸功能:

https://flaviocopes.com/jest/#setup 要在所有测试运行之前执行一次操作,请使用 beforeAll() 函数:

beforeAll(() => 
 //do something
)

要在每次测试运行之前执行某些操作,请使用 beforeEach():

beforeEach(() => 
 //do something
)

拆解 就像你可以使用 setup 一样,你也可以在每次测试运行后执行一些操作:

afterEach(() => 
 //do something
)

在所有测试结束后:

afterAll(() => 
 //do something
)

在设置函数中进行模拟并在拆卸中恢复

【讨论】:

但是如何使用 setup 和 restore 进行单次测试? 如果您只想在特定情况下模拟,那么不幸的是,您还需要在该测试中恢复。它可能又长又慢,但您可以分别模拟所有人并在每次测试之前/之后恢复所有内容(即 beforeEach() 中的所有模拟)这不是最好的解决方案,但这是我过去必须做的事情

以上是关于如何清除 Jest 模拟实现以进行下一次测试?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 jest.mock 模拟 useRef 和反应测试库

使用 MSW 和 Jest 时如何在请求之间的测试中清除 RTK 查询缓存?

如何模拟在使用 Jest 测试的 React 组件中进行的 API 调用

Jest - 模拟和测试 node.js 文件系统

使用 Jest、AudioContext 进行测试和模拟

用 Jest 模拟 Firebase 功能(单元测试)