您可以轻松地要求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测试中,我会像这样做:
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 + "');");
}
});
}
@Test public void serverPingTest() {
driver.executeScript("_setJunitTestid_('MainAppTest.serverPingTest');");
waitForTest(driver, "MainAppTest.serverPingTest");
}
请注意,如果需要,您可以多次调用
waitForTest
,以便在任何时候需要测试暂停直到所有异步操作完成。