Since March 2016, the aws sdk for javascript/nodejs does provide the .promise() method on AWS.Request link.
Now I'm running some unit test on the AWS.DynamoDB.DocumentClient(...).get() method
(which returns an AWS.Request object) where I also call the .promise() method afterwards to receive a Promise.
Now when I stub the .get() method, Im already returning a Promise, so I want the .promise() method to do nothing more than returning my existing Promise (via "return this").
my code is like
var docClient = AWS.DynamoDB.DocumentClient(...)
docClient.get(...).promise().then(...)
and my test file
var docClient = AWS.DynamoDB.DocumentClient(...)
var batchGetStub = sinon.stub(docClient, 'get').callsFake(function(){return Promise.resolve({id:1337})});
// now I need to also stub the .promise() method
var promiseStub = sinon.stub(docClient.get, 'promise').callsFake(functions(){return this}); // just return the promise back
This doesn't seem to work and I'm getting back the following error
TypeError: Cannot stub non-existent own property promise
I guess I somehow need to declare a .promise method on my fake function.
I tried this with:
this.protoype.promise = function(this){return this};
inside my fake function, but this throws
TypeError: Cannot set property 'promise' of undefined
The problem here is that you're treating the get function as returning a promise - which is doesn't.
get() returns an object that has a function called promise on it, which when called returns a promise object.
So what you actually need to do is something like:
var docClient = AWS.DynamoDB.DocumentClient(...)
var batchGetStub = sinon.stub(docClient, 'get').callsFake(function(){
return {
promise: function () {
return Promise.resolve({id:1337})
}
};
});
Tristan is right in the answer above: the get() function call does not return a promise, it returns an object that has a function called "promise" on it, which when called should return a promise object.
You can use sinon for this if you want to, but in simple cases it might be preferable to just stub the object manually, as follows:
const getStub = {
get: (params) => ({
promise: async () => ({id:1337}),
})
};
You don't need to return Promise.resolve if you use the async key word, it will return the {id: 1337} object as a resolved promise by default.
One other note: you're intermingling terminology around get and batchGet. These are different dynamo document client functions with different return signatures, so be careful. Good luck!
Related
I'm creating my own package that needs to do some emitting from the package to the main file.
This simplified module gets what I'm trying to achieve:
const fetch = require("node-fetch")
module.exports.client = async function(username,password,version) {
// Do login code here
this.emit("login", loginData)
}
Then my test code runs that with
var client = new require("../index").client("username","password","0.774")
client.on("login",function(data) {
console.log(data)
})
With every time, the program returning
client.on("login",function(data) {
^
TypeError: client.on is not a function
Has anyone had a similar issue? This is my first time making a module in this style, so please let me know.
Thank you!
Because your function client() exported from index.js is declared with the async keyword, it will return a Promise rather than an actual value passed to the return statement. Assuming the value you return from the function client() is an object with an on() function, then in order to access this function you either need to await the result of client() or you need to use the .then() function on promise. Example:
var clientPromise = new require("../index").client("username","password","0.774")
clientPromise.then(client => {
client.on("login",function(data) {
console.log(data)
})
});
While you could also use await on the result of the client() function, this is not allowed in all contexts, and so you would have to wrap the code that does this in another async function.
Hey there so I'm trying to store the result of a promise in a variable because I have some code that doesn't work inside of the promise.
(I'm pretty new to node.js so a bit of explanation would be awesome!)
And how would I use a async await?
Heres my current code:
const rbx = require("noblox.js")
var myblurb;
rbx.getBlurb(166962499).then(function (blurb) {
myblurb = blurb;
});
console.log(myblurb);
The function that sets your variable will get called after your function completes, not during it. Therefore your variable is still undefined after calling rbx.getBlurb because it returns a promise that will complete later on.
Using async/await:
One way to get what you're after is to use await. This can only be done within a function that is declared as async, and that will mean the function now will also return a Promise. If you're okay with that, then you can do it like this:
async function doStuff() {
var myblurb;
myblurb = await rbx.getBlurb(166962499);
console.log(myblurb);
}
This tells your function to stop and wait until the promise from calling getBlurb has resolved, and assign the resolved value to your local variable.
Note that if there's an error (either a promise rejection or a thrown exception) then your function will pass that along. You could surround it all with a try/catch to handle that if you need.
Operating Within then
If you don't want to make your function return a promise then you could alternately do this:
rbx.getBlurb(166962499).then(function (blurb) {
const myblurb = blurb;
console.log(myblurb);
// myblurb is only useful within this 'then' resolve function
});
It just means that you have to do the rest of your functionality that relies on the myblurb variable within that resolve function of the then.
Note that whoever is calling this code needs to understand that it will be asynchronous, so it should either use a callback function or Promise to give its results back to the caller (if needed)
Your callback function where assignment is happening will be called after promise is called. Console.log will be executed before that. So it will always log undefined.
You have to do console.log inside function of you can also use async await so solve this.
exports.handler = async (event, context, callback) => {
try {
const { headers, body } = event;
//This is where I forgot the "await" keyword
const input = ValidateInput(body); //Returns Promise
callback(null, true);
}catch(err){
console.log(err);
callback(null, false);
}
}
When calling a function that returns a promise and forgetting to create an await expression promise function call, and that function rejects the promise, Lambda logs this error in cloudwatch
(node:1) UnhandledPromiseRejectionWarning: #<Object>
The fix is simple, don't forget the await expression
const input = await ValidateInput(body); //Return Promise
The fix is simple, don't forget the await expression
const input = await ValidateInput(body); //Return Promise
As already mentioned, the solution is to ensure you await the promise:
const input = await ValidateInput(body);
But I thought I'd add a little context around why this occurs.
Since Promises can be stored in variables and chained at any point, there's no way for the library to know whether or not a Promise chain will have a .catch associated with it some time in the future. Many libraries therefore have a default behaviour of writing to the console if the rejected Promise has not been handled within a number of passes of the event loop; which is why you see this in the log.
You should generally take this warning as implying that you haven't awaited something you should have. As in reality, it's rare that you'd see it on purpose.
The AWS doc explicitly says you don't use callback with an async function.
The third argument, callback, is a function that you can call in
non-async functions to send a response. The callback function takes
two arguments: an Error and a response. The response object must be
compatible with JSON.stringify.
For async functions, you return a response, error, or promise to the
runtime instead of using callback.
So, you may want to fix that in your lambda function.
See here : https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback
In my express node application written in typescript, I'm using mongoose and am trying to avoid callback hell whist allowing for stubbing the mongoose functions in my mocha/sinon unit tests.
Where mongoose does not return a promise (e.g. Model.count()) I'm wrapping the call in a new (awaited) promise which is resolved/rejected in the callback, like so:
const myModelCount = await new Promise((resolve, reject) => {
MyModel.count({}, (err, count) => {
if (err) {
reject(err);
return;
}
resolve(count);
});
});
This works great but I don't know how I can unit test the containing function and avoid a timeout error due to the unresolved promise. I want to stub MyModel.count so that it does not attempt to call the database, but how do I stub it so that the awaited promise is resolved and the program continues?
Doing something like:
sinon.stub(MyModel, 'count').returns(Promise.resolve(1));
does not work as it does not resolve the promise that is being awaited, and since I don't have access to the resolve/reject parameters I don't know how to resolve the promise in the stub.
Is there a way I can make the above work, or alternatively how I can refactor my code to both avoid callback hell and so that I can stub out the mongoose functions in unit tests?
Regarding returned promises, there seems to be a misunderstanding (easy to have with mongoose) with the model count method. It returns a query object if called without a callback and, as with all query objects, when invoked by exec() (also without a callback) will return a promise.
mongoose.Promise = global.Promise; // This sets the mongoose internal Promise to the native Promise, while it is not necessary it is highly advised.
const promise = MyModel.count().exec();
As far as testing, if the unit test is meant to test that a wrapper function is simply calling the wrapped function then I find this to be more of a functional test as any test should only be concerned with the inputs/outputs of a function (the API of the function) and not the inner workings (the implementation of the function). In other words the function should be a black box to any tests so that if the implementation were to change but the API remains constant then the tests should continue to pass.
That said if you really want to stub the model count/exec out then something like the following might work (pseudo coding here):
sinon.stub(MyModel, 'count').returns({
exec() { return Promise.resolve(1); }
});
Hope this clears up some possible confusion and helps!
I'm struggling a bit with async/await and returning a value from a Promise.
function test () {
return new Promise((resolve, reject) => {
resolve('Hello')
})
}
async function c() {
await test()
}
As I understood things I should be able to get a value by doing:
console.log(c())
But clearly I am missing a point here as this returns a promise. Shouldn't it print "hello"? On a similar note I am unclear as to whether a callback needs to be converted to a promise before wrapping it in async/await?
I am missing a point here as this returns a promise. Shouldn't console.log(c()) print "hello"?
No, async functions always return promises. They're not magically running asynchronous code synchronously - rather the reverse, they turn synchronous-looking code (albeit speckled with await keywords) into asynchronously running one.
You can get the result value inside the asynchronous function:
async function c() {
const result = await test()
console.log(result);
return 'World';
}
c().then(console.log);
I am unclear as to whether a callback needs to be converted to a promise before wrapping it in async/await?
Yes, you can await only promises. See How do I convert an existing callback API to promises? for how to do the conversion.
Async functions return a Promise. If the function throws an error, the
Promise will be rejected. If the function returns a value, the Promise
will be resolved.