我正在使用
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(),有没有办法实现这一点.
谢谢
解决方法
您可以轻松地要求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.然后,在我的硒测试中,我做了这样的事情:
// 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,只要您需要该测试暂停,直到完成所有异步操作.