I have an endpoint in API Gateway which is mapped to a Lambda function in AWS. While writing test cases for the new handler function of the endpoint, I do not want the spec file to call the actual API or connect to DynamoDB. I tried to add a sinon.stub
, but it still made the call to connect to DynamoDB and the test-case failed. I am unable to locate where the stub went wrong.
Handler.js:
saveUser(userName, logger) {
const Item = {
id: uuid.v4(),
userName,
ttl: parseInt(Date.now() / 1000) + 900 // expire the name after 15 minutes from now
};
const params = {
TableName: "my-table-name",
Item
};
logger.log(`Saving new user name to DynamoDB: ${JSON.stringify(params)}`);
return new Promise(function(resolve, reject) {
db.put(params, function(err, _) {
if (err) {
logger.exception(`Unable to connect to DynamoDB to create: ${err}`);
reject({
statusCode: 404,
err
});
} else {
logger.log(`Saved data to DynamoDB: ${JSON.stringify(Item)}`);
resolve({
statusCode: 201,
body: Item
});
}
});
});
}
Handler.spec.js:
import AWS from "aws-sdk";
const db = new AWS.DynamoDB.DocumentClient({
apiVersion: "2012-08-10"
});
describe("user-name-handler", function() {
const sandbox = sinon.createSandbox();
afterEach(() => sandbox.restore());
it("Test saveUser() method", async function(done) {
const {
saveUser
} = userHandler;
sandbox.stub(db, "put")
.returns(new Promise((resolve, _) => resolve({
statusCode: 200
})));
try {
const result = await saveUser("Sample User", {
log: () => {},
exception: () => {}
});
expect(result).to.be.equal({
data: "some data"
});
done();
} catch (err) {
console.log(err);
done();
}
});
});
Error:
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
I console-logged the err
object and it gave me this error, which made me think it is trying to connect to the DynamoDB.
Error: connect ENETUNREACH 127.0.0.1:80
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1144:16) {
message: 'Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1',
errno: 'ENETUNREACH',
code: 'CredentialsError',
syscall: 'connect',
address: '127.0.0.1',
port: 80,
time: 2023-05-07T10:45:25.835Z,
originalError: {
message: 'Could not load credentials from any providers',
errno: 'ENETUNREACH',
code: 'CredentialsError',
syscall: 'connect',
address: '127.0.0.1',
port: 80,
time: 2023-05-07T10:45:25.835Z,
originalError: [Object]
}
Related: How to test a method which returns data from AWS DynamoDB
2
Answers
The
yields()
functionInstead of the stub directly return a
Promise
, it should be chained with a.yields()
along with the parameters its callback will accept. We can change the parameters to cover various branches of the code.Code
Useful Link
https://www.youtube.com/watch?v=vXDbmrh0xDQ
You are mocking the
db
that is declared in the test file – not thedb
thatsaveUser
is actually using.The solution is to move the db declaration to its own module, say: db.js
and then import it both from the module of
saveUser
and from the test – so that we’ll be mocking the samedb
instance thatsaveUser
uses.UPDATE
I was able to run the test with the following code successfully:
The test code:
userHandler file:
package.json
OUTPUT