了解Selenium中的执行asynchronous脚本

我一直在使用selenium (通过python绑定和通过protractor )相当长的时间,每次我需要执行一个JavaScript代码,我已经使用execute_script()方法。 例如, 滚动页面 (python):

 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") 

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

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

或者,获取所有元素属性 (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) 

但是,WebDriver API也有我没有亲自使用过的execute_async_script()

它包含哪些用例? 什么时候应该使用execute_async_script()而不是常规的execute_script()

问题是selenium的具体,但语言不可知论者。

下面是对这两个API的引用 (好吧,它是Javadoc,但function是相同的),这里是摘录它,突出了差异

[executeAsyncScript]在当前选定的框架或窗口的上下文中执行一段asynchronous的JavaScript代码。 与执行同步JavaScript不同,使用此方法执行的脚本必须通过调用所提供的callback来明确表示它们已完成。 这个callback函数总是作为最后一个参数注入执行函数。

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


既然你使用了量angular器,我将以此为例。 量angular器在getwaitForAngular使用executeAsyncScript

waitForAngular ,量angular器需要等到angular度宣布所有事件都解决了。 你不能使用executeScript因为它需要在最后返回一个值(尽pipe我猜你可以实现一个不断循环的轮询循环,直到完成)。 它的工作方式是量angular器提供了一个callback,一旦所有的事件都解决了,Angular就会调用这个callback函数,而这需要executeAsyncScript。 代码在这里

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

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

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


总而言之,当您关心调用脚本中的返回值时,请使用executeAsyncScript ,但该返回值将不会立即可用。 如果你不能查询结果,这是特别必要的,但是必须使用callback或承诺来获得结果(你必须自己翻译callback)。

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

当检查浏览器端的条件时, 可以使用execute_async_script执行所有可以使用execute_async_script执行的检查。 即使你正在检查是asynchronous的。 我知道,因为曾经有一个与execute_async_script的错误,如果脚本返回结果太快,我的testing失败。 据我所知,现在错误已经消失,所以我一直在使用execute_async_script但是事先几个月,我用execute_script来执行execute_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;")) 

require调用是asynchronous的。 但是,这个问题除了把一个variables泄漏到全局空间之外,就是它增加了networking请求。 每个execute_script调用都是一个networking请求。 wait方法通过轮询工作:它运行testing,直到返回的值为真。 这意味着wait执行的每个检查的一个networking请求(在上面的代码中)。

当你在本地testing时,这不是什么大问题。 如果你必须通过networking,因为你有一个像酱汁实验室(我使用的,所以我正在谈论经验)的服务提供的浏览器,每个networking请求放慢你的testing套件。 所以使用execute_async_script不仅可以编写一个看起来更自然的testing(像我们通常使用asynchronous代码所做的那样调用callback函数,而不是泄露到全局空间中),但它也有助于testing的性能。

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

我现在看到的方式是,如果testing将钩到浏览器端的asynchronous代码来获得结果,我使用execute_async_script 。 如果要做的事情没有可用的asynchronous方法,我使用execute_script