了解Selenium中的执行异步脚本

ale*_*cxe 23 javascript python selenium selenium-webdriver protractor

我一直在使用selenium(使用python绑定并且通过protractor大部分)很长一段时间,每次我需要执行javascript代码时,我都使用了execute_script()方法.例如,对于滚动页面(python):

driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
Run Code Online (Sandbox Code Playgroud)

或者,对于另一个元素(量角器)内的无限滚动:

var div = element(by.css('div.table-scroll'));
var lastRow = element(by.css('table#myid tr:last-of-type'));

browser.executeScript("return arguments[0].offsetTop;", lastRow.getWebElement()).then(function (offset) {
    browser.executeScript('arguments[0].scrollTop = arguments[1];', div.getWebElement(), offset).then(function() {
        // assertions

    });
});
Run Code Online (Sandbox Code Playgroud)

或者,获取所有元素属性字典(python):

driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element)
Run Code Online (Sandbox Code Playgroud)

但是,WebDriver API也有execute_async_script()我个人没有使用过的.

它涵盖了哪些用例?我什么时候应该使用execute_async_script()而不是常规execute_script()

问题是硒特异性,但与语言无关.

Lou*_*uis 25

我什么时候应该使用execute_async_script()而不是常规execute_script()

当谈到在浏览器端检测的条件下,所有的检查,你可以执行execute_async_script可以被执行execute_script.即使你正在检查的是异步的.我知道因为曾经有一个错误execute_async_script导致我的测试失败,如果脚本返回的结果太快了.据我所知,这个错误现在已经消失,所以我一直在使用,execute_async_script但事先已经好几个月,我用来execute_scriptexecute_async_script更自然的任务.例如,执行检查需要加载带有RequireJS的模块来执行检查:

driver.execute_script("""
// Reset in case it's been used already.
window.__selenium_test_check = undefined;
require(["foo"], function (foo) {
    window.__selenium_test_check = foo.computeSomething();
});
""")

result = driver.wait(lambda driver: 
    driver.execute_script("return window.__selenium_test_check;"))
Run Code Online (Sandbox Code Playgroud)

require调用是异步的.然而,除了将变量泄漏到全局空间之外,问题在于它将网络请求倍增.每个execute_script呼叫都是网络请求.该wait方法通过轮询工作:它运行测试直到返回值为true.这意味着每个检查wait执行一个网络请求(在上面的代码中).

当你在本地测试时,这不是什么大问题.如果你必须通过网络,因为你正在使用像Sauce Labs这样的服务提供的浏览器(我使用它,所以我说的是经验),每个网络请求都会降低你的测试套件的速度.因此,使用execute_async_script不仅允许编写看起来更自然的测试(调用回调,就像通常使用异步代码一样,而不是泄漏到全局空间中),但它也有助于测试的性能.

result = driver.execute_async_script("""
var done = arguments[0];
require(["foo"], function (foo) {
    done(foo.computeSomething());
});
""")
Run Code Online (Sandbox Code Playgroud)

我现在看到的方式是,如果测试要挂钩到浏览器端的异步代码以获得结果,我会使用execute_async_script.如果它要做一些没有异步方法可用的东西,我会用execute_script.


han*_*uan 17

这是对两个API 的引用(它是Javadoc,但函数是相同的),这里有一个摘录,突出了差异

[executeAsyncScript]在当前选定的框架或窗口的上下文中执行异步JavaScript.与执行同步JavaScript不同,使用此方法执行的脚本必须通过调用提供的回调明确表示它们已完成.此回调始终作为最后一个参数注入到执行的函数中.

基本上,execSync阻止了由selenium浏览器执行的进一步操作,而execAsync在完成时不会阻塞和调用callback.


既然您使用了量角器,我将以此为例.量角器使用executeAsyncScript两个getwaitForAngular

在中waitForAngular,量角器需要等到角度宣布所有事件都已解决.你不能使用,executeScript因为它需要在最后返回一个值(虽然我猜你可以实现一个繁忙的循环,不断轮询角度直到它完成).它的工作方式是量角器提供一个回调,Angular会在所有事件都解决后调用,并且需要executeAsyncScript.代码在这里

get,量角器需要轮询页面,直到window.angularAngular设置全局.一种方法是driver.wait(function() {driver.executeScript('return window.angular')}, 5000),但是这样,量角器每隔几毫秒就会在浏览器上敲击.相反,我们这样做(简化):

functions.testForAngular = function(attempts, callback) {
  var check = function(n) {
    if (window.angular) {
      callback('good');
    } else if (n < 1) {
      callback('timedout');
    } else {
      setTimeout(function() {check(n - 1);}, 1000);
    }
  };
  check(attempts);
};
Run Code Online (Sandbox Code Playgroud)

同样,这需要executeAsyncScript因为我们没有立即返回值.代码在这里


总而言之,executeAsyncScript当您关心调用脚本中的返回值时使用,但该返回值将不会立即可用.如果您无法轮询结果,但是必须使用回调或承诺(您必须自己转换为回调)获取结果,这尤其必要.