Selenium is a wonderful library. It supports all major browsers, has all the features we will probably need and is currently the de-facto standard in browser tests today, and rightfully so.
(For those that don’t know, browser tests are tests that run a browser, automate the browser to interact with your frontend application, and test it that way.)
Selenium has bindings for lots of languages — Java, C#, Python, Ruby, and others. This is great — you can use your favorite language to write your tests in.
Notice how there’s no code after the
fs.readFile. The “real” code happens in the callback that is passed to
fs.readFile. This is why callbacks in NodeJS are everywhere!
Unfortunately, this makes for some really ugly and obfuscated code. Imagine needing to loop over a set of files and copying them. With callbacks, you can’t use for-loops for that — you actually need some sort of recursion to do that. Yes, Asynchronous code is not just like synchronous code with callbacks — it’s a different way of writing code.
And it’s not pretty.
To compensate for this ugliness, ES2015 added
Promise objects. Using promises, one can write callback code in a much nicer way:
This is much nicer but is still a bit obfuscated. And it still has callbacks in it (the “then” handlers). Because, in the end, NodeJS has only one thread, and you’re not allowed to block it with I/O.
Yay! async/await handles all the ugly stuff and translates the above code to code that uses promises. We don’t care — it just works. It will never block on I/O. And we can use for-loops, and while loops, and break, and continue, and try/catch, as if we were programming in synchronous mode. Just don’t forget those
await-s on functions that return a promise, or strange concurrent things start to happen.
Selenium WebDriver Code
Given all the above, imagine my surprise when I saw
this Selenium WebDriver code:
driver.get(…) line? That’s code that does I/O — it communicates via the network to another process. There is no possibility in NodeJS to run this code synchronously.
And yet, there is nary a promise, async/await, or callback in site! How does this work? Well, it turns out that it’s all a scam. You can find the sordid details
here. But what is happening really is that when you call
driver.get it doesn’t really navigate to the URL given, but rather just schedules it for later execution. And when the code calls
driver.findElement, then it doesn’t really execute that, but rather schedules it for later, after the
driver.get. It’s all scheduled to run asynchronously and not really executing synchronously.
This is wonderfully complex programming and a technological feat in itself! But why did they do it? They did it to make testers that are used to Java/C#/Python synchronous programming, not be flummoxed by asynchronous programming. So they hacked it to make it look synchronous, but execute asynchronously.
But why do I call it a hack? Looks good, no? Well, no. Unfortunately, this abstraction leaks like hell. If you’re debugging the code, and reach the
driver.get line, you expect it execute in the browser, but it doesn’t. And if you want to do an if based on the value of an element, then you can’t, because you don’t really have the value. It will get the value later.
Promise, so the above could be written thus:
This is better, as now our code has control on when things happen, but it’s still Promise code — not the nicest way to write. But at least it’s not disguising itself as synchronous—it’s running asynchronously and things work as expected.
Fortunately, we now have async/await to help us. Let’s write the above code in async/await style:
That’s it — Selenium WebDriver code that looks synchronous code, but that uses Promises underneath, and has no hack code underneath that does crazy things.
It seems that the Selenium community understands that the scam that is this synchronous/asynchronous mode should be temporary:
So start getting used to using async/await in your Selenium WebDriver tests—it’ll shortly be the only way in town.
And that’s a good thing.
To read more about Applitools’ visual UI testing and Application Visual Management (AVM) solutions, check out the resources section on the Applitools website. To get started with Applitools, request a demo or sign up for a free Applitools account.
About the Author:
30 years of experience have not dulled the fascination Gil Tayar has with software development. From the olden days of DOS, to the contemporary world of Software Testing, Gil was, is, and always will be, a software developer. He has in the past co-founded WebCollage, survived the bubble collapse of 2000, and worked on various big cloudy projects at Wix.
His current passion is figuring out how to test software, a passion which he has turned into his main job as Evangelist and Senior Architect at Applitools. He has religiously tested all his software, from the early days as a junior software developer to the current days at Applitools, where he develops tests for software that tests software, which is almost one meta layer too many for him.
In his private life, he is a dad to two lovely kids (and a cat), an avid reader of Science Fiction, (he counts Samuel Delany, Robert Silverberg, and Robert Heinlein as favorites) and a passionate film buff. (Stanley Kubrick, Lars Von Trier, David Cronenberg, anybody?)
Unfortunately for him, he hasn’t really answered the big question of his life – he still doesn’t know whether static languages or dynamic languages are best.