如何使用jest在node中模拟postgresql(pg.js



我是node.js的新手。我正在使用pg在 node.js 为 postgresql 编写代码,并为无服务器应用程序编写pg-native代码。我需要为它编写单元测试。我无法使用开玩笑或 sinon 来嘲笑pg客户。

我的实际代码是这样的

const { Client } = require('pg');
export const getAlerts = async (event, context) => {
const client = new Client({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_DATABASE,
password: process.env.DB_PASSWORD,
port: process.env.PORT
});
await client.connect();
try {
const result = await client.query(`SELECT * FROM public.alerts;`);
console.log(result.rows);
client.end();
return success({ message: `${result.rowCount} item(s) returned`, data: result.rows, status: true });
} catch (e) {
console.error(e.stack);
client.end();
return failure({ message: e, status: false });
}
};

如何在这里嘲笑pg客户?

以下是使用jestjs的单元测试解决方案:

index.js

const { Client } = require('pg');
const { success, failure } = require('./handler');
export const getAlerts = async (event, context) => {
const client = new Client({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_DATABASE,
password: process.env.DB_PASSWORD,
port: process.env.PORT,
});
await client.connect();
try {
const result = await client.query(`SELECT * FROM public.alerts;`);
console.log(result.rows);
client.end();
return success({ message: `${result.rowCount} item(s) returned`, data: result.rows, status: true });
} catch (e) {
console.error(e.stack);
client.end();
return failure({ message: e, status: false });
}
};

hander.js

export function success(data) {}
export function failure(data) {}

index.spec.js

import { getAlerts } from './';
import { Client } from 'pg';
import { success, failure } from './handler';
jest.mock('pg', () => {
const mClient = {
connect: jest.fn(),
query: jest.fn(),
end: jest.fn(),
};
return { Client: jest.fn(() => mClient) };
});
jest.mock('./handler.js', () => {
return {
success: jest.fn(),
failure: jest.fn(),
};
});
describe('59540432', () => {
let client;
beforeEach(() => {
client = new Client();
});
afterEach(() => {
jest.clearAllMocks();
});
it('should success', async () => {
client.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });
await getAlerts();
expect(client.connect).toBeCalledTimes(1);
expect(client.query).toBeCalledWith('SELECT * FROM public.alerts;');
expect(client.end).toBeCalledTimes(1);
expect(success).toBeCalledWith({ message: '0 item(s) returned', data: [], status: true });
});
it('should failure', async () => {
const mError = new Error('dead lock');
client.query.mockRejectedValueOnce(mError);
await getAlerts();
expect(client.connect).toBeCalledTimes(1);
expect(client.query).toBeCalledWith('SELECT * FROM public.alerts;');
expect(client.end).toBeCalledTimes(1);
expect(failure).toBeCalledWith({ message: mError, status: false });
});
});

100% 覆盖率的单元测试结果:

PASS  src/stackoverflow/59540432/index.spec.js (11.792s)
59540432
✓ should success (16ms)
✓ should failure (5ms)
console.log src/stackoverflow/59540432/index.js:3131
[]
console.error src/stackoverflow/59540432/index.js:3155
Error: dead lock
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:39:20
at step (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:33:23)
at Object.next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:14:53)
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:8:71
at new Promise (<anonymous>)
at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:4:12)
at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:38:24)
at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
at resolve (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
at mapper (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
at promise.then (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
at process._tickCallback (internal/process/next_tick.js:68:7)
----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
index.js |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        14.109s

源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59540432

这是一个老问题,但这里有一个新的答案:

你可以看看pg-mem,我最近发布的一个库,它模拟了一个内存中的postgres实例。

它支持大多数常见的SQL查询(但在不太频繁的语法上会失败 - 如果遇到这种情况,请提交问题(。

我在这里写了一篇关于它的文章

对于您的用例(如何将其与pg一起使用(,请参阅此 wiki 部分

这是我的设置

const { Pool } = require('pg');
// setup for to mock pg
jest.mock('pg', () => {
const mPool = {
connect: function () {
return { query: jest.fn() };
},
query: jest.fn(),
end: jest.fn(),
on: jest.fn(),
};
return { Pool: jest.fn(() => mPool) };
});
describe('test for handle relay action', () => {
let pool;
// before each test case
beforeEach(() => {
pool = new Pool();
});
// clean up after each test case done
afterEach(() => {
jest.clearAllMocks();
});
test('should test', async () => {
* implement your test here *

pool.query.mockResolvedValue({ rows: [] });
expect(pool.query).toBeCalledTimes(3);
expect(pool.query).toHaveBeenCalledWith("check some texts");
expect(pool.query).toHaveBeenCalledWith(expect.anything());
});

我不建议任何人使用它,并且可能会有一些错误,但它现在让我获得了 100% 的单元测试覆盖率......

它通过回调覆盖了池 API,而不是每个人似乎都关注的客户端 API。

jest.mock('pg')
const { Pool } = require('pg')
const mockPgPoolOn = jest.fn((_event, callback) => { callback() })
const mockPgPoolConnectClientClose = jest.fn(() => {})
const mockPgPoolConnectQueryDoneFn = jest.fn(() => {
mockPgPoolConnectClientClose()
})
let mockPgPoolConnectErr = null
let mockPgPoolConnectQueryErr = null
let mockPgPoolConnectQueryResult = null
const mockPgPoolConnectQuery = jest.fn((_text, _params, callback) => {
callback(
mockPgPoolConnectQueryErr,
mockPgPoolConnectQueryResult
)
})
const mockPgPoolConnectClient = jest.fn(() => {
return {
query: mockPgPoolConnectQuery,
on: mockPgPoolOn,
close: mockPgPoolConnectClientClose
}
})
const mockPgPoolConnect = jest.fn((callback) => {
callback(
mockPgPoolConnectErr,
mockPgPoolConnectClient(),
mockPgPoolConnectQueryDoneFn
)
})
let mockPgPoolQueryErr = null
let mockPgPoolQueryResult = null
const mockPgPoolQuery = jest.fn((_text, _params, callback) => {
callback(mockPgPoolQueryErr, mockPgPoolQueryResult)
})
Pool.mockImplementation((_config) => {
return {
connect: mockPgPoolConnect,
query: mockPgPoolQuery,
on: mockPgPoolOn
}
})
const db = require('./index') // My module that uses require('pg')

我也有处理程序要清理,因为这一切都是在我开始定义任何东西之前......

afterEach(() => {
mockPgPoolQuery.mockClear()
mockPgPoolConnect.mockClear()
// ...
mockPgPoolQueryErr = null
mockPgPoolConnectErr = null
mockPgPoolConnectQueryErr = null
mockPgPoolConnectQueryResult = null
mockPgPoolQueryResult = null
// ...
})

这似乎有很多工作,就像我说过的,虽然它给了我 1:1 与观察到的使用情况相匹配的覆盖范围,但似乎很多。

我正在使用回调 API,因为它是 pg 依赖项记录的内容......到目前为止还不喜欢它。

最新更新