如何在 beforeEach 中模拟 Knex (Knex)

How to mock Knex in beforeEach (Knex)

我绞尽脑汁想了解如何模拟第三方库。我是 TDD 的新手,因此现在所有的信息对我来说都没有意义。

我想这样测试我的 class:

// my-class.test.ts
import { MyClass } from './my-class';

describe('Testing my class', () => {
  let myClass: MyClass;
  beforeEach(() => {
    jest.mock('knex', () => jest.fn());
    const knex = import('knex').Knex;
    const transacting = jest.fn();
    const where = jest.fn(() => ({ transacting }));
    const from = jest.fn(() => ({ where }));
    const withSchema = jest.fn(() => ({ from }));
    const select = jest.fn(() => ({ withSchema }));
    knex.mockImplementation(() => ({select}));
    myClass = new MyClass(knex, ???)
  });
  test(("should return mocked value") => {
    // ???
  })
});

我想在 MyClass 中基本上测试这样的方法:

// my-class.ts
export class MyClass {
  private knex: Knex;
  private transaction: Knex.Transaction;
constructor(knex: Knex, transaction: Knex.Transaction) {
  this.knex = knex;
  this.transaction = transaction;
}
async myMethod(id: string){
  return await this.knex
    .select('name')
    .withSchema('public')
    .from('table')
    .where({ id })
    .transacting(this.transaction)
} 

首先,Typescript 不允许我这样做Knex.mockImplementation。其次,我不知道如何告诉 Jest 最后一个链式函数(交易)在不同的测试中应该 return 不同的值。

如何使用 Jest 实现此目的?

对于您的情况,最好使用依赖注入将模拟对象传递到 MyClass。所以你不需要调用 jest.mock 来模拟 knex 模块。

例如

my-class.ts:

import Knex from 'knex';

export class MyClass {
  private knex: Knex;
  private transaction: Knex.Transaction;

  constructor(knex: Knex, transaction: Knex.Transaction) {
    this.knex = knex;
    this.transaction = transaction;
  }
  async myMethod(id: string) {
    return await this.knex
      .select('name')
      .withSchema('public')
      .from('table')
      .where({ id })
      .transacting(this.transaction);
  }
}

my-class.test.ts:

import { MyClass } from './my-class';
import Knex from 'knex';

describe('63863647', () => {
  it('should pass', async () => {
    const chainable = ({
      transacting: jest.fn().mockResolvedValueOnce({ id: '1', name: 'a' }),
    } as unknown) as Knex.ChainableInterface;
    const mKnex = ({
      select: jest.fn().mockReturnThis(),
      withSchema: jest.fn().mockReturnThis(),
      from: jest.fn().mockReturnThis(),
      where: jest.fn().mockReturnValueOnce(chainable),
    } as unknown) as Knex;

    const mTransaction = ({} as unknown) as Knex.Transaction;
    const myclass = new MyClass(mKnex, mTransaction);
    const actual = await myclass.myMethod('1');
    expect(actual).toEqual({ id: '1', name: 'a' });
    expect(mKnex.select).toBeCalledWith('name');
    expect(mKnex.withSchema).toBeCalledWith('public');
    expect(mKnex.from).toBeCalledWith('table');
    expect(mKnex.where).toBeCalledWith({ id: '1' });
    expect(chainable.transacting).toBeCalledWith(mTransaction);
  });
});

带有覆盖率报告的单元测试结果:

 PASS  src/Whosebug/63863647/my-class.test.ts
  63863647
    ✓ should pass (7ms)

-------------|----------|----------|----------|----------|-------------------|
File         |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------|----------|----------|----------|----------|-------------------|
All files    |      100 |      100 |      100 |      100 |                   |
 my-class.ts |      100 |      100 |      100 |      100 |                   |
-------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.666s, estimated 13s