I am trying to mock a database call and it keeps causing the db function to return undefined.
Please take a look at my files.
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);
}
});
I am also using explicit imports for jest
import { expect, describe, it, jest, afterAll } from "@jest/globals";
Use jest.mock()
to mock db
module. Since you are calling the getDbConnection
function from the module scope, you need to mock getDbConnection
before importing the code under test. Because module-scoped code will be executed as soon as the module is imported.
mocked helper function:
provides typings on your mocked modules and even their deep methods, based on the typing of its source.
It only provides typings of TS, instead of mock modules(jest.mock()
does this work).
Besides, from the Globals docs, we know:
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'
E.g.
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');
});
});
});
test result:
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