Похоже, вы проводите интеграционные тесты, а это значит, что ваши тесты должны зависеть от реальной базы данных и других внешних сервисов и ресурсов.
Прежде чем мы проведем интеграционные тесты, мы можем написать модульные тесты. Вот модульные тесты, основанные на вашем коде:
структура папок:
.
├── api.js
├── api.spec.js
├── controller.js
├── controller.spec.js
└── db.js
api.js
:
const moment = require('moment');
const db = require('./db');
module.exports.updateProductStatus = async data => {
let updateData = {
STATUS: data.status,
UPDATED_TIMESTAMP: moment().unix()
};
let result = await db
.query('UPDATE products SET ? WHERE PRODUCT_ID = ?', [updateData, data.product_id])
.catch(handleError);
return result[0];
};
function handleError(error) {
console.error(error);
}
db.js
:
module.exports = {
async query() {
return 'real query';
}
};
controller.js
:
const apiModel = require('./api');
module.exports.updateProductStatus = async (req, res, next) => {
let result = await apiModel.updateProductStatus(req.body);
if (result.affectedRows > 0) {
res.send({
status: 'success',
message: 'product status updated.'
});
} else {
res.send({
status: 'error',
message: 'failed to update product status.'
});
}
};
Юнит-тесты:
controller.spec.js
:
const controller = require('./controller');
const apiModel = require('./api');
describe('updateProductStatus controller', () => {
const mReq = { body: {} };
const mRes = { send: jest.fn() };
beforeEach(() => {
jest.restoreAllMocks();
});
test('should send success response', async () => {
const mResult = { affectedRows: 2 };
const updateProductStatusSpy = jest.spyOn(apiModel, 'updateProductStatus').mockResolvedValueOnce(mResult);
await controller.updateProductStatus(mReq, mRes);
expect(mRes.send).toBeCalledWith({ status: 'success', message: 'product status updated.' });
expect(updateProductStatusSpy).toBeCalledWith(mReq.body);
});
test('should send error response', async () => {
const mResult = { affectedRows: 0 };
const updateProductStatusSpy = jest.spyOn(apiModel, 'updateProductStatus').mockResolvedValueOnce(mResult);
await controller.updateProductStatus(mReq, mRes);
expect(mRes.send).toBeCalledWith({ status: 'error', message: 'failed to update product status.' });
expect(updateProductStatusSpy).toBeCalledWith(mReq.body);
});
});
api.spec.js
:
const apiModel = require('./api');
const db = require('./db');
describe('api', () => {
beforeEach(() => {
jest.restoreAllMocks();
});
test('should update product status', async () => {
const mData = { product_id: 1, status: 'no store' };
const mResult = [{ affectedRows: 1 }];
const querySpy = jest.spyOn(db, 'query').mockResolvedValueOnce(mResult);
const actualValue = await apiModel.updateProductStatus(mData);
expect(actualValue).toEqual(mResult[0]);
expect(querySpy).toBeCalledWith('UPDATE products SET ? WHERE PRODUCT_ID = ?', [
{ STATUS: 'no store', UPDATED_TIMESTAMP: expect.any(Number) },
1
]);
});
test('should handle error', async () => {
const mData = { product_id: 1, status: 'no store' };
const mError = new Error('connection error');
const querySpy = jest.spyOn(db, 'query').mockRejectedValueOnce(mError);
const errorSpy = jest.spyOn(console, 'error');
await expect(apiModel.updateProductStatus(mData)).rejects.toThrowError(
new TypeError("Cannot read property '0' of undefined")
);
expect(querySpy).toBeCalledWith('UPDATE products SET ? WHERE PRODUCT_ID = ?', [
{ STATUS: 'no store', UPDATED_TIMESTAMP: expect.any(Number) },
1
]);
expect(errorSpy).toBeCalledWith(mError);
});
});
Результат модульного теста для controller.spec.js
:
PASS src/stackoverflow/58692161/controller.spec.js
updateProductStatus controller
✓ should send success response (10ms)
✓ should send error response (2ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 2.792s, estimated 3s
Результат модульного теста для api.spec.js
:
PASS src/stackoverflow/58692161/api.spec.js
api
✓ should update product status (14ms)
✓ should handle error (13ms)
console.error node_modules/jest-mock/build/index.js:860
Error: connection error
at /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:22:20
at step (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:33:23)
at Object.next (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:14:53)
at /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:8:71
at new Promise (<anonymous>)
at Object.<anonymous>.__awaiter (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:4:12)
at Object.<anonymous> (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:20:31)
at Object.asyncJestTest (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
at resolve (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
at mapper (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
at promise.then (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.506s
Исходный код: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58692161