I am testing a JavaScript API using Java/Selenium.
I issue these commands on Java side,
executor.executeScript("setName('Hello');");
// Thread.sleep(2000);
String output = (String)executor.executeScript("return getName()");
assertEquals(output, "Hello");
In JavaScript side, this is an async function, so it is supposed to take some time and set the variable.
function setName(msg) {
// simulate async call
setName(function(){name = msg;},1000);
}
I need to wait for this async function to complete before moving to the next line in Java, where the assertEquals()
is executed.
Is there any way to accomplish this without using Thread.sleep()
on Java side.
Thanks
You can easily ask Selenium to wait until a particular condition is true; in exactly what you have, one alternative would be:
new FluentWait<JavascriptExecutor>(executor) {
protected RuntimeException timeoutException(
String message, Throwable lastException) {
Assert.fail("name was never set");
}
}.withTimeout(10, SECONDS)
.until(new Predicate<JavascriptExecutor>() {
public boolean apply(JavascriptExecutor e) {
return (Boolean)executor.executeScript("return ('Hello' === getName());");
}
});
However, then you're basically testing exactly what you just coded, and that has the disadvantage that if name
were set before you called setName
, you haven't necessarily waited setName
to finish. One thing I've done in the past for similar things is this:
In my testing library (which replaces real async calls with setTimeout
shims), I have this:
window._junit_testid_ = '*none*';
window._junit_async_calls_ = {};
function _setJunitTestid_(testId) {
window._junit_testid_ = testId;
}
function _setTimeout_(cont, timeout) {
var callId = Math.random().toString(36).substr(2);
var testId = window._junit_testid_;
window._junit_async_calls_[testId] |= {};
window._junit_async_calls_[testId][callId] = 1;
window.setTimeout(function(){
cont();
delete(window._junit_async_calls_[testId][callId]);
}, timeout);
}
function _isTestDone_(testId) {
if (window._junit_async_calls_[testId]) {
var thing = window._junit_async_calls_[testId];
for (var prop in thing) {
if (thing.hasOwnProperty(prop)) return false;
}
delete(window._junit_async_calls_[testId]);
}
return true;
}
In the rest of my library, I use _setTimeout_
instead of window.setTimeout
whenever I need to set something up to happen later. Then, in my selenium test, I do something like this:
// First, this routine is in a library somewhere
public void waitForTest(JavascriptExecutor executor, String testId) {
new FluentWait<JavascriptExecutor>(executor) {
protected RuntimeException timeoutException(
String message, Throwable lastException) {
Assert.fail(testId + " did not finish async calls");
}
}.withTimeout(10, SECONDS)
.until(new Predicate<JavascriptExecutor>() {
public boolean apply(JavascriptExecutor e) {
return (Boolean)executor.executeScript(
"_isTestDone_('" + testId + "');");
}
});
}
// Inside an actual test:
@Test public void serverPingTest() {
// Do stuff to grab my WebDriver instance
// Do this before any interaction with the app
driver.executeScript("_setJunitTestid_('MainAppTest.serverPingTest');");
// Do other stuff including things that fire off what would be async calls
// but now call stuff in my testing library instead.
// ...
// Now I need to wait for all the async stuff to finish:
waitForTest(driver, "MainAppTest.serverPingTest");
// Now query stuff about the app, assert things if needed
}
Note that you can call waitForTest
multiple times if needed, any time you need that test to pause until all async operations are finished.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With