如何在beforeEachKnex中模拟Knex

问题描述

我正在努力尝试了解如何嘲笑第三方库。我对TDD完全陌生,因此那里的所有信息现在对我来说都没有意义。

我要以此方式测试我的课程:

// 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)
} 

首先,打字稿不允许我做Knex.mockImplementation。其次,我不知道如何告诉Jest,最后一个链接函数(正在执行)应该在不同的测试中返回不同的值。

如何用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/stackoverflow/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