I wrote a function I try to test with jest:
asyncUpperCase(value: string): Promise<string> {
return new Promise(function(resolve, reject) {
setTimeout(() => {
return resolve(value.toUpperCase())
}, 1000);
})
}
It recives a string and turns it into a upperCase string (asynchronally).
I have to tests:
it('upperCase with promise and then', () => {
return myAsync.asyncUpperCase('hello world').then(value => {
expect(value).toBe('HELLO WORLD');
})
})
it('upperCase with promise and resolve', () => {
const promise = myAsync.asyncUpperCase('hello world');
expect(promise).resolves.toBe('HELLO WORLD');
})
First test works correctly. But the second test does never fail. So if I change the expected string to ABC it still exits with success. Why and how do i use resolves correctly?
A promise is asynchronous and needs time to be resolved, there's no way how resolves can fail synchronous test. Without being chained inside a test, it will result in unhandled rejection if test run is long enough.
It should be:
await expect(promise).resolves.toBe('HELLO WORLD');
Jest provides 3 options to properly handle asynchronous tests documentation
Returning a promise
it('upperCase with promise and resolve', () => {
const promise = myAsync.asyncUpperCase('hello world');
return expect(promise).resolves.toBe('HELLO WORLD');
})
Using async/await
Which is quite the same as an async function will return Promise<void>
it('upperCase with promise and resolve', async () => {
const promise = myAsync.asyncUpperCase('hello world');
expect(await promise).toBe('HELLO WORLD');
})
Using a callback
it('upperCase with promise and resolve', (done) => {
const promise = myAsync.asyncUpperCase('hello world');
promise
.then(result => expect(promise).toBe('HELLO WORLD'))
.then(done);
})
also you may take a look at Advance Timers by Time
Related
I want to test that a promise does not resolve in jasmine. It appears that I might be able to use not.toBeResolved(), but when I do I get a timeout error:
it('should not resolve', async () => {
const unresolvablePromise = new Promise((resolve, reject) => {});
await expectAsync(unresolvablePromise).not.toBeResolved();
});
Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)
I expected this would be the idea of not.toBeResolved - that if the promise hadn't resolved in the timeout period the test would pass.
I am guessing .not.toBeResolved is a catch 22 where it never resolves because it is awaiting for the promise and then asserting it.
I would do something like this:
// add the done callback
it('should not resolve', (done) => {
// assign a variable to determine if the promise was resolved
let promiseResolved = false;
const unresolvablePromise = new Promise((resolve, reject) => {});
// call Jasmine fail if it does resolve thereby failing the test
// and update the variable to true
unresolvablePromise.then(() => {
promiseResolved = true;
fail();
});
// 500ms (ample time for the promise to resolve), expect the variable to be false
// and call done
setTimeout(() => {
expect(promiseResolved).toBeFalse();
done();
}, 500);
});
If you expect the promise to be in a pending state, use:
await expectAsync(promise).toBePending();
https://jasmine.github.io/api/3.7/async-matchers.html#toBePending
I'm trying to make a test with jest and promises.
When I try to write in console the result get it from the promise I've got an undefined value
This is my testing code:
describe("Search concurrent with promises", () => {
describe("Test 10: Several searches in DDBB", () => {
function connection(){
let ddbb = new PGMappingDDBB();
return new Promise((resolve, reject) => {
let db = ddbb.connect();
//resolve("Hello World");
resolve(db);
});
}
test("Test 12: Several searches concurrently", () => {
connection().then(ddbb => {
console.log(ddbb);
});
});
});
});
ddbb.connect() is an asynchronous function. The code of connect() is:
async connect(){
this.client = await poolMapping.connect();
}
When I try to write the state of the variable ddbb I've got that is undefined.
However, If I comment resolve(db) and remove the comment of resolve("Hello World"), when I write the value of ddbb I've got "Hello World".
What am I doing wrong?
Edit I:
ddbb.connect() returns a Promise. If I write what returns console.log(ddbb.connect()). I've got:
Promise { <pending> }
To explain:
async connect(){
this.client = await poolMapping.connect();
// NOTE: There is no return value
}
describe("Search concurrent with promises", () => {
describe("Test 10: Several searches in DDBB", () => {
function connection(){
let ddbb = new PGMappingDDBB();
return new Promise((resolve, reject) => {
let db = ddbb.connect();
// ^^ this will be undefined, as connect has no return value
resolve(db);
// ^^^^^^^^^^^^ Resolving the promise with undefined
});
}
test("Test 12: Several searches concurrently", () => {
connection().then(ddbb => {
console.log(ddbb);
/// ^^^^ will be undefined
});
});
});
});
Additionally, jest is not waiting for the outcome of the promise, and you are not asserting anything in the test, so it will always succeed.
When dealing with promises, if you return the promise from the test, Jest will wait for it. But you still need to assert on the value.
Why is it exactly that the a resolveing promise correctly waits for the someOtherPromise to complete, but the reject does not? Run the following code sample and check the console.log output. I expected the "myFailingPromise rejected" message to show 2000 ms later, just as the "myPromise resolved" did.
let someOtherPromise = (previousPromise) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(previousPromise + ' => someOtherPromise after 2000ms');
resolve('someOtherPromise');
}, 2000);
});
}
let myPromise = () => {
return new Promise((resolve, reject) => {
resolve(someOtherPromise('myPromise'));
});
};
let myFailingPromise = () => {
return new Promise((resolve, reject) => {
reject(someOtherPromise('myFailingPromise'));
});
};
myPromise().then((val) => {
// this is executed after `someOtherPromise` resolves.
console.log('myPromise resolved');
}).catch((err) => {
console.log('myPromise rejected');
});
myFailingPromise().then((val) => {
// this is executed after `someOtherPromise` resolves.
console.log('myFailingPromise resolved');
}).catch((err) => {
console.log('myFailingPromise rejected');
});
I know the intended behaviour can be achieved by using someOtherPromise.then(reject) in the second example, but my question is why the promise as an argument to reject is not possible, since it works for resolve.
When you're resolving a promise A, if the value with which you're resolving it is a promise B, then your promise A will match the state of the promise B.
However, when you're rejecting a promise, your only giving a reason, whether the reason looks or not like a promise doesn't matter.
What you need to do, is to resolve with someOtherPromise in both cases.
If you want to wait for the first promise and reject anyway, you can do this:
let myFailingPromise = () => {
return new Promise((resolve, reject) => {
someOtherPromise.then(reject);
});
};
The reject only takes in a reason to highlight the error. Not another promise. You can resolve a promise with another promise of the same type, yet only the last promise's success case you will see.
Have a look at this adapted implementation:
const someOtherPromise = new Promise((resolve, _) => {
resolve("I am a success");
});
const failingPromise = new Promise((_, reject) => {
reject("I failed for a reason");
});
someOtherPromise
.then((result) => {
console.log("some other promise resolves", result);
failingPromise
.then((success) => {
console.log("never called");
})
.catch((reason) => {
console.error("failing promise rejects", reason);
});
}
)
.catch((error) => {
console.error("also never called", error);
});
This is the then-able approach to wait for other promises leading to a callback hell. This is why you can also use async / await syntax:
const app = async () => {
try {
const success1 = await someOtherPromise; // will succeed
console.log(success1);
const success2 = await failingPromise; // never succceds
console.log(success2); // never be reached
} catch (e) {
return Promise.reject(e); // catches the error of failing promise and rethrows it, redundant but here showcases error handling
}
}
;
app()
.then(() => {
console.log("never be reached because of failing promise");
})
.catch(console.error);
In regards to your updated question with the timeout, here is what you could do in order to always await another promise:
const otherPromise = async (willBeSuccesful: boolean) => {
console.log("started timer for case", willBeSuccesful);
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("resolve timer for case", willBeSuccesful);
const successValue = "Fnord"; // whatever you want
return willBeSuccesful
? resolve(successValue)
: reject("this other promise failed because of reasons"); // only provide a reason, not another promise
});
};
};
const alwaysWaitForOtherPromiseThenRejectAnyway = async (otherPromise) => {
try {
const success = await otherPromise; // always waits 2 seconds, not matter
console.log("other promises succeeded with value", success);
} catch (e) {
return Promise.reject(e); // passing through reason, redundant, only to showcase
}
return Promise.reject("reason why this promise failed"); // only happens after otherPromise was resolved, you could swallow that error and fail here or resolve here as well
};
const succeedingPromise = otherPromise(true);
const failingPromise = otherPromise(false);
alwaysWaitForOtherPromiseThenRejectAnyway(succeedingPromise)
.catch((reason) => console.error(reason)); // fail after waiting for success of other promise
alwaysWaitForOtherPromiseThenRejectAnyway(failingPromise)
.catch((reason) => console.error(reason)); // will fail as soon as otherPromise fails
It will always wait for the timeout before rejection happens. Rejection will have different reasons. Output will be:
started timer for case true
started timer for case false
resolve timer for case true
resolve timer for case false
other promises succeeded with value Fnord
reason why this promise failed
this other promise failed because of reasons
We are testing a call to a function that returns a Promise<void>. How can we wait until the promise resolves and then do some test?
The standard approach of then() off of the function does not work, because a Promise<void> is not thenable. So we have resorted to the following, which works but seems non-ideal.
Initial Approach
it("it does something...", function (done) {
function wrappedPromise() {
functionThatReturnsPromiseVoid(someArg);
return new Promise((resolve) => resolve());
}
wrappedPromise()
.then(() => {
expect(someVar).toBe("someValue");
done();
});
});
Subsequent Approach
let wrapVoidPromise = (wrapped): Promise<any> => new Promise((resolve) => {
wrapped();
resolve();
});
it("it does something...", function (done) {
wrapVoidPromise(() => functionThatReturnsPromiseVoid(someArg))
.then(() => {
expect(someVar).toBe("someValue");
done();
});
});
How can we do this without needing to wrap the unchainable promise?
Edits
Here is a GitHub link to the actual test. It includes this call:
route.navigationStrategy(instruction) // returns Promise<void>
Here is a GitHub link to the function. The implementation is buried somewhere inside the source to aurelia-router. Here is its interface:
navigationStrategy?: (instruction: NavigationInstruction) => Promise<void>|void;
You may try changing wrapUnchainedPromise as below :
function wrapUnchainedPromise(): Promise<any> {
return route.navigationStrategy(instruction);
}
I know how to do it in Mocha but want to know how to do it with Jasmine.
I tried this
describe('test promise with jasmine', function() {
it('expects a rejected promise', function() {
var promise = getRejectedPromise();
// return expect(promise).toBe('rejected');
return expect(promise.inspect().state).toBe('rejected');
});
});
However, the state is always pending and, of course, the test fails. I couldn't find any example online that I could make it work.
Can someone please help me with this?
Thanks.
you can now use expectAsync()
Expecting success:
it('expect result', async () => {
...
await expectAsync(someAsyncFunction(goodInput)).toBeResolved(expectedResponse)
})
Expecting failure:
it('expect result', async () => {
...
await expectAsync(someAsyncFunction(badInput)).toBeRejectedWith(expectedResponse)
})
To test asynchronous code with jasmine you should use its async syntax, e.g.:
describe('test promise with jasmine', function(done) {
var promise = getRejectedPromise();
promise.then(function() {
// Promise is resolved
done(new Error('Promise should not be resolved'));
}, function(reason) {
// Promise is rejected
// You could check rejection reason if you want to
done(); // Success
});
});
jasmine 2.7 onwards supports returning promises, and would have its fulfilled state tested.
To test for rejection:
it('test promise with jasmine', async () => {
try {
await getRejectedPromise();
} catch (err) {
return;
}
throw new Error('Promise should not be resolved');
});
or yet:
it('test promise with jasmine', async () => {
await getRejectedPromise()
.then(
() => Promise.reject(new Error('Promise should not be resolved')),
() => {});
});
To verify the actual message, besides the usual instanceof/toBe(), place inside the catch:
expect(() => { throw err }).toThrow(new MyCustomError('Custom error message'));
The benefit from this approach is to have a nicer fail message on the test output.
Expected function to throw MyCustomError: Custom error message, but it threw Another error message.
Somewhat better than the usual output.
To test for resolved (can't be simpler):
it('test promise with jasmine', async () => {
await getRejectedPromise();
});
You can use finally block to test promise state:
it('should resolve if auth succeed', (done)=>{
var p = server.login('user', 'password');
p.finally(()=>{
expect(p.isFulfilled()).toBeTruthy();
done();
});
});
You can use isFulfilled to check if promise was fulfilled and value method to check the fulfillment value. Corresponding methods for rejection are isRejected and reason.
#Leonid's answer is correct, but you can simplify like so, and use only promises:
it('test promise with jasmine', function() {
return getRejectedPromise().then(function() {
// Promise should not be resolved, so we reject it
return Promise.reject(new Error('Promise should not be resolved'));
})
.catch(function(err){
if(!/Promise should not be resolved/.test(err && err.message)){
return Promise.reject(err);
}
})
})