Reputation: 3873
I am working on write node js express js API test case using jasmine but I have one problem with my test case it's only working with live database connection and its return actual value from the database so in future may be data will change so at that time test case will fail.
So I want to create a mock of my controller and database query model
Here is my controller code
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."
})
}
};
and here is my MySQL database query module
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];
};
Here is my test case to call API and send request data
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);
});
});
please guide me how to get a response without connecting MySQL database when run test case, if there is an NPM library for mock, let me know event better if you provide sample code
thanks
Upvotes: 1
Views: 1511
Reputation: 102207
It seems you are doing the integration tests which means your tests need to depend on the real database and other external services and resources.
Before we are doing the integration tests, we can write unit tests. Here is the unit tests based on your code:
folder structure:
.
├── 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.'
});
}
};
Unit tests:
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);
});
});
Unit test result for 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
Unit test result for 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
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58692161
Upvotes: 3