【问题标题】:Does Selenium wait for JavaScript to complete?Selenium 是否等待 JavaScript 完成?
【发布时间】:2014-05-27 20:13:21
【问题描述】:

我正在使用 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() 的情况下实现这一点。

谢谢

【问题讨论】:

    标签: java javascript selenium


    【解决方案1】:

    我怀疑硒确实如此。它只等待页面完全加载,而不是脚本完成。您可能希望定义自己的“测试步骤”以等待结果(并不断轮询页面内容/脚本状态),但请务必设置合理的超时时间,以免您的测试因脚本错误而永远挂起。

    【讨论】:

      【解决方案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 shims 替换真正的异步调用)中,我有这个:

      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 来设置以后发生的事情。然后,在我的硒测试中,我做了这样的事情:

      // 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,只要您需要暂停该测试,直到所有异步操作完成。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-28
        • 2018-09-20
        • 2021-10-08
        • 1970-01-01
        • 1970-01-01
        • 2019-03-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多