我有一个网页,其中包含一些JavaScript并执行一些Ajax调用。当尝试使用Selenium测试它时,我随机收到"PHPUnit_Extensions_Selenium2TestCase_WebDriverException:元素不再附加到DOM"消息,也许每5次运行一次。
现在我知道 Ajax 调用和测试引擎之间的竞争问题,我已经采取措施防止它,但我仍然有一些问题。我的场景是这样的:我更改了选择元素 1 的值,这会触发 Ajax 调用,该调用删除选择元素 2 的所有选项子元素,并根据 Ajax 响应生成新的选项子元素。测试代码:
$this->select($this->byId('select1'))->selectOptionByValue('value1');
$this->myWaitForElementToAppear('#select2>option[value="value2"]');
$this->select($this->byId('select2'))->selectOptionByValue('value2');
最后一行触发错误。下面是 myWaitForElementToAppear 方法:
public function myWaitForElementToAppear($selector, $limit = 5) {
$start = time();
while(true) {
if($start + $limit < time()) {
break;
}
try {
$this->byCssSelector($selector);
break;
} catch(PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {}
}
}
如果我没记错的话,myWaitForElementToAppear 方法应该确保 jQuery 在退出之前已经添加了所需的选项,从而允许它在下一行使用。我应该补充一点,我已经确保这里不会发生超时(因为我的方法允许它发生),而且我很肯定
事实并非如此编辑:我应该补充一点,在myWaitForElementToAppear调用之后放置sleep(1)可以解决问题,但我不明白为什么需要额外的一秒钟。调用myWaitForElementToAppear不应该足够吗?
这里有一些解释:
首先,time() 的精度非常低,只返回 整整几秒钟过去了,这使得整个事情变得相当安静 模糊。其次,PHP 必须坐在那里循环数千次,而 它等待,基本上什么都不做。一个更好的解决方案是使用 两个脚本睡眠函数之一,sleep() 和 usleep(),它们 将暂停执行的时间量作为其唯一参数。
从 php.net:
睡眠和休眠的想法是让CPU闲置运行几个 循环,以便其他程序可以运行自己的一些循环。 什么导致更好的响应时间和更低的整体系统负载。 所以如果你必须等待什么,去睡觉几秒钟 而不是占用CPU,而什么都不做,而是 等待。
你可以从 PHPUnit 使用 waitTill :
/* waitElementToDisappear */
$this->waitUntil(function($testCase) {
try {
$input = $testCase->byCssSelector("#select2>option[value="value2"]");
} catch (PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {
if (PHPUnit_Extensions_Selenium2TestCase_WebDriverException::NoSuchElement == $e->getCode()) {
return true;
}
}
}, 5000);
/* waitElementToAppear */
$this->waitUntil(function($testCase) {
try {
$input = $testCase->byCssSelector("#select2>option[value="value2"]");
return true;
} catch (PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {}
}, 5000);