What is the correct way to test if a function gets aborted by returning?
myFunction (parameter) {
if (typeof parameter === 'object') return
// doing any stuff
}
Test (jestJS)
it('myFunction() should return if no valid parameter is passed', () => {
// SETUP
wrapper = shallow(<Component />)
// EXECUTE
wrapper.instance().myFunction(undefined)
// VERIFY
// ??
})
What is the correct way to test if a function gets aborted by returning?
The only things you can do are:
Have the function return different values depending on the code path, and test the return value. (Your function as shown doesn't have a return value on either path, so the result of calling it is undefined, but if doing any stuff includes return something and something is guaranteed not to be undefined, you could test for that.)
Test for any side-effects the function has.
E.g., unless you provide a means of knowing what happened inside the function, there's no way to tell from outside it.
A function will always return - unless it throws an exception (The correct way to abort). If function aborts correctly with an exception, one can assert whether that exception was thrown or not.
If you'd like to test that a method executes to a particular line of code within the method. Assuming you have jasmine on board, you could create a spy on an object within the execution line, and test that spy with assertions.
Otherwise, there is no real way to distinguish between an abort by returning - unless the function is expected to return something and returns nothing.
Related
The function worke perfectly, but if I want to print the content of the user, I receive in the firebase log this info:
Function returned undefined, expected Promise or value
The function is:
exports.accountCreate = functions.auth.user().onCreate(user => {
console.log("--->"+user.data);
console.log("ok");
});
Why the user.data is not able to retrieved the informations?
Thanks
Cloud Functions run in a container in a managed environment. The environment tries to minimize how long it keeps the container running, and to be able to do so, it must know when your function is done. Normally in JavaScript code is done when the last } has executed, but this gets more complex when you also need to consider asynchronous operations. For this reason Cloud Functions expects you to inform it when the function is done, in the case of functions.auth.user().onCreate by returning a value or promise.
When you explicitly return a value, it is clear that the function is done. When you explicitly return a promise, it's clear that the function needs to remain active until the promise is resolved/rejected. When you don't return a value, it is not clear what state the function is in.
In your case the fix is simple, and you for example just return true before the final }.
exports.accountCreate = functions.auth.user().onCreate(user => {
console.log("--->"+user.data);
console.log("ok");
return true;
});
The actual value is meaningless btw, a return null would work just as well.
I have a one method that looks like this:
doSomething(){
return somethingPromisy().then((blerp) => {
// do something with blerp
return blerp; // Modified, of course
});
};
Then I have another method that looks like this:
doSomethingElse(){
stepOne();
var x = stepTwo();
var y = stepThree(x);
doSomething.then((data) => {
stepFour(data + y);
});
};
I'm using mocha+chai+sinon to test this code, in particular, doSomethingElse, and then I want to make some assertions - but how can I guarantee that the promise will be resolved by the time I make assertions? I know one option would be to change it to:
doSomethingElse(){
/* ... */
return doSomething.then(...);
};
If I do this, then it's pretty easy to write my test because then it's:
return doSomethingElse().then(() => {
someFake.lastCall.args.should.deep.equal(expectedData);
});
In my test and everything is fine. But should I be returning the promise simply for the sake of returning the promise? I don't actually care about any kind of return value from doSomethingElse - I only care that when I'm testing that the function in doSomething.then was called, i.e. the doSomething promise has been resolved by the time I do my assertions.
So what's the "best" way to go about this?
You should be testing doSomething and doSomethingElse separately with doSomethingElse using a mock of doSomething, with a mocked then methd that can return your value for testing. You basically want to test them separately not at the same time. You're basically coupling doSomething and doSomethingElse in this case which makes testing harder. Testing should be easy, you aren't testing to see if promises work, so use a fake one to move on to testing logic!
I do not find a lack of return value to be an issue myself, there is a return value, you're just chaining it, not storing it.
Before async/await, when my code used callbacks, I was able to do three things: (1) call the callback with a result, (2) call the callback with an Error, or (3) not call the callback at all.
Case (3) was used in situations like this: say that you have a zoom button and a user can click it to render an image at a higher resolution, and this is an async process. If the user clicks the zoom button again, then the first render is no longer relevant, and can be canceled to let the new zoom level render run. I handled this by returning from inside the function without calling the callback, e.g.
if (process.wasCanceled()) {
return;
}
// ...
callback(someResult);
With async/await, there are only two things that you can do: return or throw. Currently, I've been using throw to indicate that the operation was canceled, since returning can falsely indicate that upstream processes should keep running. But the problem with throwing is that all the upstream callers need to know that it's not really an error, per se, and so they may need to check the type of the error.
Another crazy idea I had was to create a promise that never returns. E.g. await never(), where the function is defined like this:
async function never () {
return new Promise(function () {});
}
That is sort of the equivalent of not calling a callback.
But I don't know if that would just leak memory over and over.
Is there a better equivalent without the drawbacks I mentioned above?
If absolutely necessary, you can await a promise that never returns. This is the equivalent of not calling a callback.
async function never () {
return new Promise(function () {});
}
async function op (process) {
// ...
if (process.wasCanceled()) await never();
// ...
}
According to these answers, this will be garbage collected, because the returned promise is never used and there are no connections to the heap inside the promise's function argument.
Do never resolved promises cause memory leak?
Are JavaScript forever-pending promises bad?
However, this is most likely not what you want to do, since upstream callers may like to know that their operation has been canceled. If the operation was initiated by a user through the UI, then yes, canceling without telling the caller is probably OK, but if the operation was initiated programmatically and cancelled some other way, e.g. by the user, then the calling code might need to know that, so that it can try again, or clean up resources.
For this reason, the solution is to throw an error, of a specific class so that the caller can detect that the process was cancelled. E.g.
class ProcessCanceledError extends Error {
...
}
async function render (process) {
while (...) {
// do some rendering
await delay(20);
if (process.wasCanceled()) throw new ProcessCanceledError();
}
}
var zoomProcess;
async function zoom () {
let process = new Process();
if (zoomProcess != null && !zoomProcess.isDone()) {
zoomProcess.cancel();
}
try {
await render();
} catch (e) {
// or you could do e.process === process
if (e instanceof ProcessCanceledError &&
process.wasCanceled() // make sure it was actually ours
) {
// this assumes we are a top level function
// otherwise, you would want to propagate the error to caller's caller
return;
}
throw e;
}
}
I'm writing an Angular 2 RC5 application, and unit testing using Karma and Jasmine.
I have a method that returns a Promise<Foo> (It's on top of a call to angular's http.post) I want to run some assertions after that finishes.
Something like this doesn't work
let result = myService.getFoo();
result.then(rslt => expect(1+1).toBe(3)); // the error is lost
This creates an 'Unhandled Promise rejection' warning, but the error is suppressed and the test passes. How do I run assertions based on my resolved promise?
Notes:
The .catch() method doesn't seem to be what I'm after. I don't want to log or do anything that continues normal program flow, I want to fail the test.
I've seen code that looks like $rootScope.$digest();. I'm not sure what the typescript equivalent of this sort of thing is. There doesn't seem to be a way of saying: "I have a promise, I'm going to wait here until I have a synchronous result".
The test should look something like this:
it('should getFoo', function (done) {
let result = myService.getFoo();
result
.then(rslt => expect(rslt).toBe('foo'))
.then(done);
});
Using the done callback works, but you should be able to do this as well:
(Note the return)
it('should getFoo', function () {
let result = myService.getFoo();
return result
.then(rslt => expect(rslt).toBe('foo'))
});
Mocha website states:
"To make things even easier, the done() callback accepts an error, so we may use this directly: [see their example]"
So lets try that:
it('works',function(done){
expect(1).to.be(1)
done( new Error('expected error') )
})
/* Insert the error manually for testing and clarity. */
run it and:
1 failing
1) works:
Error: expected error
at Context.<anonymous>
[stack trace]
How do we make the test pass when the error response is the desired result?
That's not async. The callback function is just there for you to inform mocha that you're testing async code and so mocha shouldn't run the next test until you call the callback function. This is an example of how to use the callback function:
it('works',function(done){
setTimeout(function(){
// happens 0.5 seconds later:
expect(1).to.be(1);
done(); // this tells mocha to run the next test
},500);
});
Also, mocha does not handle any form of exceptions, async or otherwise. It leaves that up to an exception library of your choosing. In your case you're using expect.js? If so, expect handles expected errors via the throwException method (also called throwError):
it('throws an error',function(done){
expect(function(){
throw new Error('expected error');
}).to.throwError(/expected error/);
});
Now, in general async code in node.js don't throw errors. They pass errors to the callback as parameters instead. So to handle async errors you can simply check the err object:
// using readFile as an example of async code to be tested:
it('returns an error',function(done){
fs.readFile(filename, function (err, data) {
expect(err).to.be.an(Error);
done(); // tell mocha to run next test
})
});
So use to.throwError() if you're checking synchronous errors and to.be.an(Error) if you're checking async errors.
Additional notes:
The first time I saw this I was stumped. How can mocha know that the test is synchronous or asynchronous when the only difference is weather the function you pass to it accepts an argument or not? In case you're like me and are scratching your head wondering how, I learned that all functions in javascript have a length property that describes how many arguments it accepts in its declaration. No, not the arguments.length thing, the function's own length. For example:
function howManyArguments (fn) {
console.log(fn.length);
}
function a () {}
function b (x) {}
function c (x,y,z) {}
howManyArguments(a); // logs 0
howManyArguments(b); // logs 1
howManyArguments(c); // logs 3
howManyArguments(howManyArguments); // logs 1
howManyArguments(function(x,y){}); // logs 2
So mocha basically checks the function's length to determine weather to treat it as a synchronous function or asynchronous function and pauses execution waiting for the done() callback if it's asynchronous.
Even more additional notes:
Mocha, like most other js unit test runners and libraries, work by catching errors. So it expects functions like expect(foo).to.be.an.integer() to throw an error if the assertion fails. That's how mocha communicates with assertion libraries like expect or chai.
Now, as I mentioned above, a common idiom in node is that async functions don't throw errors but passes an error object as the first argument. When this happens mocha cannot detect the error and so can't detect a failing test. The work-around to this is that if you pass the error object from the async code to the callback function it will treat it the same as a thrown error.
So, taking one of my examples above:
it('executes without errors',function(done){
fs.readFile(filename, function (err, data) {
done(err); // if err is undefined or null mocha will treat
// it as a pass but if err is an error object
// mocha treats it as a fail.
})
});
Or simply:
it('executes without errors',function(done){
fs.readFile(filename,done);
});
Strictly speaking, this feature is a bit redundant when used with libraries like expect.js which allows you to manually check the returned error object but it's useful for when your assertion library can't check the error object (or when you don't really care about the result of the async function but just want to know that no errors are thrown).
You can also return your async such as promise as below.
it('Test DNA', () => {
return resolvedPromise.then( (result)=>{
expect(result).to.equal('He is not a your father.');
},(err)=>{
console.log(err);
});
});