使用 ts-jest/utils 模拟数据库 Jest

Mock database Jest using ts-jest/utils

我正在尝试模拟数据库调用,它一直导致 db 函数 return 未定义。

请看一下我的文件。

db.ts

import * as mysql from "mysql";
import * as util from "util";

{... other functions with named exports}

const getDbConnection = () => {
  const pool = mysql.createPool(DB_CONFIG);
  return {
    query(sql: string) {
      return util.promisify(pool.query).call(pool, sql);
    },
  };
};
export default getDbConnection;

testname.spec.ts

import { mocked } from "ts-jest/utils";
import db from "../src/utils/db";

jest.mock("../src/utils/db");
describe("Test Controller", () => {
  afterAll(() => {
    jest.resetAllMocks();
  });

  mocked(db);

  it("should retrieve all", async () => {
    await request(app)
      .get("/data")
      .expect(200)
      .expect(function (res) {
        expect(res.body.data).toContain("data");
      });
  });
});

controller.ts

import getDbConnection from "../utils/db";

const db = getDbConnection();

router.get("/", async (_, res) => {
  let sql = "...";

  try {
    let result = await db.query(sql);
    res.status(200).json(result);
  } catch (error) {
    console.log("DB error", error);
  }
});

我也在开玩笑使用显式导入

import { expect, describe, it, jest, afterAll } from "@jest/globals";

使用jest.mock()模拟db模块。由于您是从模块范围调用 getDbConnection 函数,因此您需要在导入被测代码之前模拟 getDbConnection 。因为模块范围内的代码将在导入模块后立即执行。

mocked 辅助函数:

provides typings on your mocked modules and even their deep methods, based on the typing of its source.

它只提供 TS 的类型,而不是模拟模块(jest.mock() 做这个工作)。

此外,从 Globals 文档中,我们知道:

In your test files, Jest puts each of these methods and objects into the global environment. You don't have to require or import anything to use them. However, if you prefer explicit imports, you can do import {describe, expect, test} from '@jest/globals'

例如

db.ts:

import * as mysql from 'mysql';
import * as util from 'util';

const DB_CONFIG = {};

const getDbConnection = () => {
  const pool = mysql.createPool(DB_CONFIG);
  return {
    query(sql: string) {
      return util.promisify(pool.query).call(pool, sql);
    },
  };
};
export default getDbConnection;

controller.ts:

import getDbConnection from './db';
import express from 'express';

const app = express();
const db = getDbConnection();

app.get('/', async (_, res) => {
  let sql = 'select * from users';

  try {
    let result = await db.query(sql);
    res.status(200).json(result);
  } catch (error) {
    console.log('DB error', error);
  }
});

export { app };

controller.spec.ts:

import request from 'supertest';
import { mocked } from 'ts-jest/utils';
import getDbConnection from './db';

jest.mock('./db');

const mGetDbConnection = mocked(getDbConnection);

describe('68825658', () => {
  afterAll(() => {
    jest.resetAllMocks();
  });
  it('should retrieve all', async () => {
    const mDB = {
      query: jest.fn().mockResolvedValueOnce('data'),
    };
    mGetDbConnection.mockReturnValueOnce(mDB);

    const { app } = require('./controller');
    await request(app)
      .get('/')
      .expect(200)
      .expect((res) => {
        expect(res.body).toEqual('data');
      });
  });
});

测试结果:


 PASS  examples/68825658/controller.spec.ts (10.715 s)
  68825658
    ✓ should retrieve all (434 ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |      80 |      100 |      50 |   78.95 |                   
 controller.ts |   91.67 |      100 |     100 |   90.91 | 14                
 db.ts         |    62.5 |      100 |       0 |    62.5 | 7-10              
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        11.624 s