Selenium waiting for JavaScript to complete?

I am testing the JavaScript API using Java / Selenium.

I issue these commands from the Java side,

executor.executeScript("setName('Hello');"); // Thread.sleep(2000); String output = (String)executor.executeScript("return getName()"); assertEquals(output, "Hello"); 

On the JavaScript side, this is an asynchronous function, so it is assumed that it takes 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 assertEquals() is assertEquals() .

Is there a way to do this without using Thread.sleep() on the Java side.

thanks

+7
java javascript selenium
source share
2 answers

You can easily ask Selenium to wait until a specific condition is met; in what you have, one of the options:

 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 basically test exactly what you just encoded, and this has the disadvantage that if name were set before you called setName , you did not have to wait for setName to complete. One thing I did in the past for such things is this:

In my test library (which replaces real asynchronous calls with setTimeout plugins), 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 when I need to install something that happens later. Then, in my test for selenium, 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 if necessary, you can call waitForTest several times, anytime you need this test to pause the action until all async operations are complete.

+2
source share

I doubt selenium. It only expects the page to be fully loaded, but not a script to complete. You can define your β€œtest step” to wait for the result (and constantly poll the contents of the page / script), but be sure to set a reasonable time so that you don't test your tests forever with a script error.

0
source share

All Articles