2015-01-20 2 views
18

Я использую seleniumpython bindings и через protractor главным образом) в течение достаточно долгое времени, и каждый раз, когда мне нужно было выполнить яваскрипт кода, я использовал execute_script() метод. Например, for scrolling the page (питон):Понимание выполнения асинхронного скрипта в Selenium

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

Или, infinite scrolling inside an another element (транспортир):

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 

    }); 
}); 

Или, для получения dictionary of all element attributes (питон):

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) 

Но API WebDriver также имеет execute_async_script(), который я лично не использовал.

В каких случаях он распространяется? Когда следует использовать execute_async_script() вместо обычного execute_script()?

Вопрос является селен-специфическим, но язык-агностик.

ответ

13

Вот reference к двум API (ну это Javadoc, но функции те же), а вот отрывок из него, что подчеркивает разницу

[executeAsyncScript] Выполнить асинхронный кусок JavaScript в контекст выбранного кадра или окна. В отличие от , выполняющих синхронный JavaScript, скрипты, выполненные с помощью этого метода, должны явно сигнализировать, что они завершены вызовом предоставленного обратного вызова. Этот обратный вызов всегда вводится в исполняемую функцию в качестве последнего аргумента.

В основном, execSync блокирует дальнейшие действия выполняются с помощью селена браузера, в то время как execAsync не блокирует и вызовов на callback, когда это сделано.


Поскольку вы работали с транспортиром, я буду использовать его в качестве примера. Транспортир использует executeAsyncScript в обоих get и waitForAngular

waitForAngular В, транспортир должен ждать, пока Угловая не объявляет, что все события улажен. Вы не можете использовать executeScript, потому что это должно вернуть значение в конце (хотя, я думаю, вы можете реализовать цикл занятости, который постоянно проверяет углы, пока это не будет выполнено). Способ, которым он работает, заключается в том, что транспортир обеспечивает обратный вызов, который вызывает Угловые вызовы после того, как все события установлены, и для этого требуется executeAsyncScript. Код here

get, транспортир должен опросить страницу, пока глобальный window.angular не установлен на угловой. Один из способов сделать это - 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); 
}; 

Опять же, это требует executeAsyncScript, потому что мы не имеем возвращаемое значение немедленно.Код here


В общем, используйте executeAsyncScript, когда вы заботитесь о возвращаемом значении в вызывающем скрипте, но возвращаемое значение не будут доступны немедленно. Это особенно необходимо, если вы не можете опросить результат, но должны получить результат с помощью обратного вызова или обещания (которое вы должны перевести на обратный вызов самостоятельно).

+0

Благодарим вас за ссылку на javaDoc - есть некоторые примеры. Вам лично нужно было выполнить скрипт async? Если да, не могли бы вы описать варианты использования? Это было бы очень полезно. – alecxe

+0

Добавлены некоторые примеры. Надеюсь, это имеет смысл. – hankduan

+0

Отлично, это было интересное путешествие в сценарии клиентской стороны транспортира и другие внутренние детали! Еще раз спасибо. Теоретически предположим: если бы я тестировал угловое приложение с привязками selenium python - мне нужно было бы загрузить похожие (или такие же) клиентские скрипты и вызывать функции testForAngular() 'и' waitForAngular() 'асинхронно, чтобы проверить и подождать, пока Угловой станет «стабильным». Верный? – alecxe

20

Когда я должен использовать execute_async_script() вместо обычного execute_script()?

Когда дело доходит до проверки условий на стороне браузера, все чеки, которые можно выполнить с execute_async_script может быть выполнена с execute_script. Даже если то, что вы проверяете, является асинхронным. Я знаю, потому что когда-то была ошибка с execute_async_script, которая заставила мои тесты потерпеть неудачу, если скрипт слишком быстро вернул результаты. Насколько я могу судить, ошибка исчезла, поэтому я использовал 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 вызова асинхронно. Проблема с этим, хотя, помимо утечки переменной в глобальное пространство, заключается в том, что она умножает сетевые запросы. Каждый вызов execute_script является сетевым запросом. Метод wait работает путем опроса: он запускает тест до тех пор, пока возвращаемое значение не будет истинным. Это означает, что один сетевой запрос на проверку выполняется wait (в коде выше).

Когда вы тестируете локально, это не имеет большого значения. Если вам нужно пройти через сеть, потому что у вас есть браузеры, предоставляемые службой, такой как Sauce Labs (которую я использую, поэтому я говорю по опыту), каждый сетевой запрос замедляет ваш тестовый набор. Таким образом, использование execute_async_script не только позволяет написать тест, который выглядит более естественным (вызов обратного вызова, как мы обычно делаем с асинхронным кодом, а не утечка в глобальное пространство), но это также помогает в выполнении ваших тестов.

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

Как я вижу сейчас, что если тест будет вклиниться в асинхронном код на стороне браузера, чтобы получить результат, я использую execute_async_script. Если он собирается сделать что-то, для которого нет асинхронного метода, я использую execute_script.

+0

Превосходно. +1 для подробного описания того, когда по-прежнему целесообразно использовать 'execute' над' executeAsync'. Я бы добавил оговорку: эта ошибка для меня (с использованием веб-драйвера селена и javascript), казалось бы, вернулась. – GrayedFox

Смежные вопросы