I read the official jest documentation on async/await as well as numerous blog posts dealing with the topic. All are describing different ways of awaiting a value on which assertions are made as well as waiting for the test function to complete. None of them included the following pattern, however I don't see why it should cause trouble: Assume I have a
const getValue: () => Promise<string>
and that I'm testing it with
test("await in expect", async () => {
expect(await getValue()).toBe("b")
})
I can't reproduce the issue with an implementation like
const getValue = () => new Promise((resolve) => {
setTimeout(() => {
resolve("b")
}, 4000)
})
however I'm experiencing about 20% fails of an integration test where getValue runs queries against a real database because afterAll function is called early and terminates the connection.
I'm aware that I can overcome this with resolves and other use of await or done or else, e.g.
test("await in expect", async () => {
await expect(getValue()).resolves.toBe("b")
})
and I already managed to overcome the issue in my real world integration test with this approach.
However, I'd like to broaden my understanding of what's going on and what I'm doing when using await inside expect().
I'm using Jest 27.
There's a good reason why one should create real reproducers for issues and questions: The issue might turn out to be something completely different.
In this case the TypeORM Repository.save promise apparently returns before the saved instance is flushed into the database or cache or whatever which is queried by Repository.find.
I might investigate further or just go with a 100ms sleep after save. And yes, you can quote that in yet another blog post about why ORMs are troublesome.
Related
Follow my previous article Built method time is not a function, I managed to successfully implement the functions with an appropriate wait time by following a combination of #ggorlen's comment and #Konrad Linkowski answer, additionally, this article puppeteer: wait N seconds before continuing to the next line that #ggorlen answered in, this comment especially helped: -
Something else? Run an evaluate block and add your own code to wait for a DOM mutation or poll with setInterval or requestAnimationFrame and effectively reimplement waitForFunction as fits your needs.
Instead I incorporated waitForSelector, produces the following script:
const puppeteer = require('puppeteer')
const EXTENSION = '/Users/usr/Library/Application Support/Google/Chrome/Profile 1/Extensions/gidnphnamcemailggkemcgclnjeeokaa/1.14.4_0'
class Agent {
constructor(extension) {
this._extension = extension
}
async runBrowser() {
const browser = await puppeteer.launch({
headless:false,
devtools:true,
args:[`--disable-extensions-except=${this._extension}`,
`--load-extension=${this._extension}`,
'--enable-automation']
})
return browser
}
async getPage(twitch) {
const page = await (await this.runBrowser()).newPage()
await page.goto('chrome-extension://gidnphnamcemailggkemcgclnjeeokaa/popup.html')
const nextEvent = await page.evaluate(async () => {
document.getElementById('launch-trace').click()
})
const waitSelector = await page.waitForSelector('.popup-body')
const finalEvent = (twitch) => new Promise(async (twitch) => page.evaluate(async (twitch) => {
const input = document.getElementById('user-trace-id')
input.focus()
input.value = twitch
}))
await finalEvent(twitch)
}
}
const test = new Agent(EXTENSION)
test.getPage('test')
However, my webpage produces undefined rather than test, I am a little confused by the parameters twich and k, and how to properly assert the parameter twitch so its entered inside the function finalEvent.
Alternatively, I have also tried wrapping finalEvent into a Promise so I can assert the parameter twitch into it as a function, but this does not fill any value:
const finalEvent = (val) => new Promise(async () => await page.evaluate(async () => {
const nextTime = () => new Promise(async () => setInterval(async () => {
const input = document.getElementById('user-trace-id')
input.focus()
input.value = val
}, 3000))
//await nextTime(k)
}))
await finalEvent(twitch)
There are a few issues here. First,
const page = await (await this.runBrowser()).newPage()
hangs the browser handle and leaks memory which keeps the process alive. Always close the browser when you finish using it:
const browser = await this.runBrowser();
const page = await browser.newPage();
// ... do your work ...
await browser.close();
Here, though, Puppeteer can throw, again leaking the browser and preventing your app from cleanly exiting, so I suggest adding a try/catch block with a finally block that closes the browser.
Generally speaking, try to get the logic working first, then do a refactor to break code into functions and classes. Writing abstractions and thinking about design while you're still battling bugs and logical problems winds up making both tasks harder.
Secondly, there's no need to async a function if you never use await in it, as in:
const nextEvent = await page.evaluate(async () => {
document.getElementById('launch-trace').click()
})
Here, nextEvent is undefined because evaluate()'s callback returned nothing. Luckily, you didn't attempt to use it. You also have const waitSelector = await page.waitForSelector('.popup-body') which does return the element, but it goes unused. I suggest enabling eslint no-unused-vars, because these unused variables make a confusing situation worse and often indicate typos and bugs.
On to the main problem,
const finalEvent = (twitch) => new Promise(async (twitch) => page.evaluate(async (twitch) => {
const input = document.getElementById('user-trace-id')
input.focus()
input.value = twitch
}))
await finalEvent(twitch)
There are a number of misunderstandings here.
The first is the age-old Puppeteer gotcha, confusing which code executes in the browser process and which code executes in the Node process. Everything in an evaluate() callback (or any of its family, $eval, evaluateHandle, etc) executes in the browser, so Node variables that look like they should be in scope won't be. You have to pass and return serializable data or element handles to and from these callbacks. In this case, twitch isn't in scope of the evaluate callback. See the canonical How can I pass a variable into an evaluate function? for details.
The second misunderstanding is technically cosmetic in that you can make the code work with it, but it's a serious code smell that indicates significant confusion and should be fixed. See What is the explicit promise construction antipattern and how do I avoid it? for details, but the gist is that when you're working with a promise-based API like Puppeteer, you should never need to use new Promise(). Puppeteer's methods already return promises, so it's superfluous at best to wrap more promises on top of the them, and at worst, introduces bugs and messes up error handling.
A third issue is that the first parameter to new Promise((resolve, reject) => {}) is always a resolve function, so twitch is a confusing mislabel. Luckily, it won't matter as we'll be dispensing with the new Promise idiom when using Puppeteer 99.9% of the time.
So let's fix the code, keeping these points in mind:
await page.evaluate(twitch => {
const input = document.getElementById('user-trace-id');
input.focus();
input.value = twitch;
},
twitch
);
Note that I'm not assigning the return value to anything because there's nothing being returned by the evaluate() callback.
"Selecting, then doing something with the selected element" is such a common pattern that Puppeteer provides a handy method to shorten the above code:
await page.$eval("#user-trace-id", (input, twitch) => {
input.focus();
input.value = twitch;
},
twitch
);
Now, I can't run or reproduce your code as I don't have your extension, and I'm not sure what goal you're trying to achieve, but even the above code looks potentially problematic.
Usually, you want to use Puppeteer's page.type() method rather than a raw DOM input.value = ..., which doesn't fire any event handlers that might be attached to the input. Many inputs won't register such a change, and it's an untrusted event.
Also, it's weird that you'd have to .focus() on the input before setting its value. Usually focus is irrelevant to setting a value property, and the value will be set either way.
So there may be more work to do, but hopefully this will point you in the right direction by resolving the first layer of immediate issues at hand. If you're still stuck, I suggest taking a step back and providing context in your next question of what you're really trying to accomplish here in order to avoid an xy problem. There's a strong chance that there's a fundamentally better approach than this.
I've written a lot of asynchronous unit tests lately, using a combination of Angular's fakeAsync, returning Promises from async test body functions, the Jasmine done callback, etc. Generally I've been able to make everything work in a totally deterministic way.
A few parts of my code interact in deeply-tangled ways with 3rd party libraries that are very complex and difficult to mock out. I can't figure out a way to hook an event or generate a Promise that's guaranteed to resolve after this library is finished doing background work, so at the moment my test is stuck using setTimeout:
class MyService {
public async init() {
// Assume library interaction is a lot more complicated to replace with a mock than this would be
this.libraryObject.onError.addEventListener(err => {
this.bannerService.open("Load failed!" + err);
});
// Makes some network calls, etc, that I have no control over
this.libraryObject.loadData();
}
}
it("shows a banner on network error", async done => {
setupLibraryForFailure();
await instance.init();
setTimeout(() => {
expect(banner.open).toHaveBeenCalled();
done();
}, 500); // 500ms is generally enough... on my machine, probably
});
This makes me nervous, especially the magic number in the setTimeout. It also scales poorly, as I'm sure 500ms is far longer than any of my other tests take to complete.
What I think I'd like to do, is be able to tell Jasmine to poll the banner.open spy until it's called, or until the test timeout elapses and the test fails. Then, the test should notice as soon as the error handler is triggered and complete. Is there a better approach, or is this a good idea? Is it a built-in pattern somewhere that I'm not seeing?
I think you can take advantage of callFake, basically calling another function once this function is called.
Something like this:
it("shows a banner on network error", async done => {
setupLibraryForFailure();
// maybe you have already spied on banner open so you have to assign the previous
// spy to a variable and use that variable for the callFake
spyOn(banner, 'open').and.callFake((arg: string) => {
expect(banner.open).toHaveBeenCalled(); // maybe not needed because we are already doing callFake
done(); // call done to let Jasmine know you're done
});
await instance.init();
});
We are setting a spy on banner.open and when it is called, it will call the callback function using the callFake and we call done inside of this callback letting Jasmine know we are done with our assertions.
So i have this code below. It deletes the db and adds two users for test case.
when i verify it manually in mongo database everything shows correct but in mocha test case I get the timeout error even after defining the done argument and calling it.
Please help me on this.
const users = [{
_id: new ObjectID(),
email: 'First'
}, {
_id: new ObjectID(),
email: 'Second'
}];
beforeEach((done) => {
User.remove({}).then(() => {
return User.insertMany(users);
}).then(() => done());
})
In mocha, tests will time out after 2000 ms by default. Even if you were handling the asynchrony 100% correctly (which you are not), if you do an async operation that takes longer than 2 seconds, mocha will assume a failure. This is true even if the async operation is in a beforeEach or some other hook.
To change this, you need to invoke the timeout method on the test instance, giving it a sufficiently-high value. To access the test instance, you need to define your test functions using the function keyword rather than arrow syntax, and it will be available as this in your test functions:
beforeEach(function(done) {
this.timeout(6000); // For 6 seconds.
User.remove({}).then(() => {
return User.insertMany(users);
}).then(() => done());
});
In what way could you handle the asynchrony here better, though? As Henrik pointed out in comments, you'll never call done if either of your database calls fail. To be honest, though, since you're already dealing with promises, you shouldn't even use the done callback. Instead, just use Mocha's built-in promise support by returning the chained promise.
beforeEach(function() {
this.timeout(6000); // For 6 seconds.
return User.remove({})
.then(() => User.insertMany(users));
});
This way, if either of those promises rejects, Mocha will know and will show the rejection instead of just sitting around waiting for your test to time out.
You can even use async/await instead if you prefer:
beforeEach(async function() {
this.timeout(6000); // For 6 seconds.
await User.remove({});
await User.insertMany(users);
});
Could you please help me to understand javascirpt async hell?
I think I am missing something important ☹ The thing is that js examples and most of the answers on the internet are related to just one part of code – a small snippet. But applications are much more complicated.
I am not going write it directly in JS since I am more interested of the design and how to write it PROPERLY.
Imagine these functions in my application:
InsertTestData();
SelectDataFromDB_1(‘USERS’);
SelectDataFromDB_2(‘USER_CARS’,’USERS’);
FillCollections(‘USER’,’USER_CARS’);
DoTheWork();
DeleteData();
I did not provide any description for the functions but I think it is obvious based on names. They need to go in THIS SPECIFIC ORDER. Imagine that I need to run a select into the db to get USERS and then I need run a select to get USER_CARS for these USERS. So it must be really in this order (consider the same for other functions). The thing is that need to call 6 times Node/Mysql which is async but I need results in specific order. So how can I PROPERLY make that happen?
This could work:
/* not valid code I want to present the idea and keep it short */
InsertTestData(
Mysql.query(select, data, function(err,success)
{
SelectDataFromDB_1(‘USERS’); -- in that asyn function I will call the next procedure
}
));
SelectDataFromDB_1 (
Mysql.query(select, data, function(err,success)
{
SelectDataFromDB_2(‘USERS’); -- in that asyn function I will call the next procedure
}
));
SelectDataFromDB_2 (
Mysql.query(select, data, function(err,success)
{
FillCollections (‘USERS’); -- in that asyn function I will call the next procedure
}
));
etc..
I can “easily” chain it but this looks as a mess. I mean really mess.
I can use some scheduler/timmers to schedule and testing if the previous procedure is done)
Both of them are mess.
So, what is the proper way to do this;
Thank you,
AZOR
If you're using a recent version of Node, you can use ES2017's async/await syntax to have synchronous-looking code that's really asynchronous.
First, you need a version of Mysql.query that returns a promise, which is easily done with util.promisify:
const util = require('util');
// ...
const query = util.promisify(Mysql.query);
Then, wrap your code in an async function and use await:
(async () => {
try {
await InsertTestData();
await SelectDataFromDB_1(‘USERS’);
await SelectDataFromDB_2(‘USER_CARS’,’USERS’);
await FillCollections(‘USER’,’USER_CARS’);
await DoTheWork();
await DeleteData();
} catch (e) {
// Handle the fact an error occurred...
}
})();
...where your functions are async functions, e.g.:
async InsertTestData() {
await query("INSERT INTO ...");
}
Note the try/catch in the async wrapper, it's essential to handle errors, because otherwise if an error occurs you'll get an unhandled rejection notice (and future versions of Node may well terminate the process). (Why "unhandled rejections"? Because async functions are syntactic sugar for promises; an async function returns a promise.) You can either do that with the try/catch shown, or alternate by using .catch on the result of calling it:
(async () => {
await InsertTestData();
// ...
})().catch(e => {
// Handle the fact an error occurred
});
Background
I have a nodejs server running and I installed the promise package which follows the promise api specs.
Since I succeeded in making denodeify(fn, length) work, I am now in the process of chaining promises, but I am failing to grasp the main concepts.
What I tried
By reading the documentation example on the specification page, I reached the following code:
let Promise = require("promise");
let write = Promise.denodeify(jsonfile.writeFile);
let read = Promise.denodeify(jsonfile.readFile);
read("dataFile.txt").then( () => {
write("./testFile.txt", "hello_World", TABS_FORMATTING).then(console.log("file complete"));
});
Which is quite different from the examples I see, for example, in the Solutions Optimist tutorial:
loadDeparture( user )
.then( loadFlight )
.then( loadForecast );
Objective
My objective is to make my code as beautiful as the example I showed before, but I don't understand how I can make as concise as it is right now.
Question
1 - What changes do I need to perform in my code to achieve that level?
The given example uses named function to make it look as good as it can get, but that can be a bit redundant because then you're creating functions for every little thing in the chain. You must pick and choose when to use named functions over anonymous functions.
One thing you must also realize is that to chain promises you must return them.
So to make it a proper chain you must return the write method so it is passed down to the next step.
Also make sure that the catch() method is used at the bottom of every promise chain so that errors aren't silently swallowed.
Note that in the example here I'm using the ES2015 arrow functions to return the write() method as that makes it looks better(which seemed to be the purpose of your question).
let Promise = require("promise");
let write = Promise.denodeify(jsonfile.writeFile);
let read = Promise.denodeify(jsonfile.readFile);
read("dataFile.txt")
.then(() => write("./testFile.txt", "hello_World", TABS_FORMATTING))
.then(results => console.log("file complete", results))
.catch(error => console.error(err));
I'd recommend reading this article for some best practices.
Nesting promises kind of defeats the purpose because it creates pyramid code (just like callbacks).
The main concept that may be escaping you is that you can return inside a then and the returned value (can be a promise or a value) can then be accessed in a chained then:
read("dataFile.txt").then( () => {
return write("./testFile.txt", "hello_World", TABS_FORMATTING);
}).then( () => {
console.log("file complete");
});
Now, you can extract the functions:
function writeTheFile() {
return write("./testFile.txt", "hello_World", TABS_FORMATTING);
}
function consoleLog() {
console.log("file complete");
}
read("dataFile.txt")
.then(writeTheFile)
.then(consoleLog);