Selenium会等待JavaScript执行完毕吗?

7

我正在使用Java / Selenium测试JavaScript API。

我在Java端发出以下命令:

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

在 JavaScript 方面,这是一个异步函数,因此应该花费一些时间并设置变量。
function setName(msg) {
    // simulate async call 
    setName(function(){name = msg;},1000);
}

在Java中,在执行assertEquals()之前,我需要等待此异步函数完成。

有没有不使用Java端的Thread.sleep()来实现这个的方法。

谢谢

2个回答

2
您可以轻松地要求Selenium等待特定条件成立;在您目前的情况下,一种选择是:
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());");
  }
});

然而,这样你基本上测试的就是你刚刚编写的代码,这有一个缺点,如果在调用 setName 之前设置了 name ,则未必等待 setName 完成。我过去解决类似问题的方法是:

在我的测试库中(它使用 setTimeout 假数据替代真正的异步调用),我有这样一个功能:

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;
}

在我余下的库中,每当我需要设置稍后发生的操作时,我使用_setTimeout_而不是window.setTimeout。然后,在我的Selenium测试中,我会像这样做:
// 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
}

请注意,如果需要,您可以多次调用waitForTest,以便在任何时候需要测试暂停直到所有异步操作完成。

0

我怀疑Selenium并没有等待脚本完成,只是等待页面完全加载。你可能希望定义自己的“测试步骤”来等待结果(并不断轮询页面内容/脚本状态),但务必设置合理的超时时间,以免在脚本错误上永远挂起你的测试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接