Issue
I am new to Jest and unit testing, I have an express API deployed on serverless(Lambda) on AWS.Express api uses dynamodb for crud operations
Note:- my api is based out of express and not just plain node, because on jest website they are telling ways for plain nodejs
I am able to do unit test on express on the methods which doesnt use dynamodb.However it fails for the methods which are using dynamodb, as to my understanding this has something to do with dynamodb being remote, because the code present in app.js corresponds to dyanmo db which is hosted on aws using lambda.
How do I go about it?
Note:- my api is based out of express and not just plain node
const isUrl = require('is-url');
const AWS = require('aws-sdk');
const { nanoid } = require('nanoid/async');
const express = require('express');
const router = express.Router();
const dynamoDb = new AWS.DynamoDB.DocumentClient();
// URL from users
router.post('/', async (req, res, next) => {
// urlId contains converted short url characters generated by nanoid
const urlId = await nanoid(8);
const { longUrl } = req.body;
// Veryfying url Format using isUrl, this return a boolean
const checkUrl = isUrl(longUrl);
if (checkUrl === false) {
res.status(400).json({ error: 'Invalid URL, please try again!!!' });
}
const originalUrl = longUrl;
const userType = 'anonymous'; // user type for anonymous users
const tableName = 'xxxxxxxxxxxxx'; // table name for storing url's
const anonymousUrlCheckParams = {
TableName: tableName,
Key: {
userId: userType,
originalUrl,
},
};
dynamoDb.get(anonymousUrlCheckParams, (err, data) => {
const paramsForTransaction = {
TransactItems: [
{
Put: {
TableName: tableName,
Item: {
userId: userType,
originalUrl,
convertedUrl: `https://xxxxxxxxxxxxxxxx/${urlId}`,
},
},
},
{
Put: {
TableName: tableName,
Item: {
userId: urlId,
originalUrl,
},
ConditionExpression: 'attribute_not_exists(userId)',
},
},
],
};
if (err) {
console.log(err);
res
.status(500)
.json({ error: 'Unknown Server Error, Please Trimify Again!' });
} else if (Object.keys(data).length === 0 && data.constructor === Object) {
dynamoDb.transactWrite(paramsForTransaction, async (error) => {
if (error) {
// err means converted value as userId is repeated twice.
console.log(error);
res
.status(500)
.json({ error: 'Unknown Server Error, Please trimify again. ' });
} else {
res.status(201).json({
convertedUrl: `https://xxxxxxxxxxxx/${urlId}`,
});
}
});
} else {
res.status(201).json({
convertedUrl: data.Item.convertedUrl,
});
}
});
});
module.exports = router;
my test.js
const request = require('supertest');
const app = require('../app');
test('Should convert url from anonymous user ', async () => {
await request(app)
.post('/anon-ops/convert')
.send({
longUrl: 'https://google.com',
})
.expect(201);
});
Solution
First off, if you’re wanting to do unit testing. It doesn’t really matter much if you’re using express js or not, hence, the examples and information on the jest website are very valid to get you on your way.
How easy it is to do unit testing, mostly depends on how you have structured your code. For example, you could keep all your express js specific code in separate files and then only instantiate the files holding your actual business logic (which some might call a services layer) during your unit tests. That’s at least one way where you could make it easier on yourself. Using a functional approach also makes your code easier to test or at the very least using dependency injection, so you can swap out dependencies during testing in order to test some functionality in isolation.
When it comes to DynamoDB, you’ve got two options. Either mocking or running a local version.
You can either mock the specific functions you’re calling either using the jest mocks or using a mocking library such as sinon. Whichever you choose is mostly personal preference.
The second option is running a local version of DynamoDB in a docker container. This has the upside of also verifying your actual calls to the DynamoDB service (which you could do by verifying the mocks, but it’s easy to make a mistake in the verification), however, it is more cumbersome to set up and your tests will be slower, so this might skew your test to be more integration tests than unit tests (but that distinction is an evening worth or arguing in itself).
If you want to go towards end-to-end testing of the entire API, you can have a look at the SuperTest NPM package.
(Edit) Added small example using sinon
const AWS = require('aws-sdk');
const sinon = require('sinon');
const ddb = new AWS.DynamoDB.DocumentClient();
const getStub = sinon.stub(AWS.DynamoDB.DocumentClient.prototype, "get");
getStub.callsFake((params, cb) => {
cb(null, {result: []});
});
ddb.get({foo: 'bar'}, (err, val) => {
console.log(val); // => { "result": [] }
})
Answered By – stijndepestel
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0