我正在使用jasmine进行写节点js express js 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."
})
}
};
这是我的MySQL数据库查询模块
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];
};
这是我调用API和发送请求数据的测试用例
it("should update product status", () => {
var reqData = {
"account_name" : "demo",
"last_update": 1571298222,
"product_detail": {
"QUENTITY":"36","PRICE":"5.69527","TITLE":"AAAAAAAAA","SKU":"A10512","UPC":""
},
"sku": "A10512AAA",
"status": "inactive",
"product_id": 1
};
request.post(
{
url: 'localhost:9090/api/update-product-status',
headers: {
'authorization': 'Bearer eyJhbGciOiJIUzI1NiddIsInR5cCIdd'
},
form: reqData
}, function (err, httpResponse, body) {
console.log("TCL: err", err);
console.log("TCL: body", body);
console.log("TCL: httpResponse", httpResponse);
});
});
请指导我如何在运行测试用例时在不连接MySQL数据库的情况下获得响应,如果有用于模拟的NPM库,请提供示例代码让我更好地了解事件
谢谢
答案 0 :(得分:1)
您似乎正在执行集成测试,这意味着您的测试需要依赖于实际的数据库以及其他外部服务和资源。
在进行集成测试之前,我们可以编写单元测试。这是基于您的代码的单元测试:
文件夹结构:
.
├── 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