一旦我的测试试图从一个页面移动到另一个页面,我真的很难弄清楚如何阻止PHPUnit + Selenium的破坏。例如,我做这样的事情:
public function myTest()
{
$this->clickOnElement('somelink');
$this->assertEquals('content', $this->byId('newElement'));
}
当我尝试运行此操作时,我收到如下错误:
1) myTestClass::myTest
PHPUnit_Extensions_Selenium2TestCase_WebDriverException: Element not found in the cache
- perhaps the page has changed since it was looked up
Command duration or timeout: 107 milliseconds
问题仅在两个页面之间移动时出现,并且您要查找的元素存在于两个页面上(如标题或标题。)这里实际上存在两个问题,这两个问题都是竞争条件:
clickOnElement()
开始下一页加载,但在此之前,byId()
在当前页上运行并找到该元素。然后浏览器完成加载新页面,assertContains()
尝试从元素中获取值。但是现在已经加载了新页面,我们的元素引用来自之前的页面。因为当前DOM中不再存在该元素,所以您会收到有关陈旧元素的错误。
clickOnElement()
启动下一页加载,但在完成加载byId()
之前,新的,但未完全加载的页面上运行。由于页面尚未完成加载,因此会出现“未找到元素”错误。
如何解决这两个问题?
答案 0 :(得分:2)
好的,我想我想出了这个。第二个问题(在页面加载之前查找元素)可以通过设置implicitWait()
值来修复:
public function setUpPage()
{
// If an element cannot be found because the page is still loading, keep
// trying for 5000ms before failing
$this->timeouts()->implicitWait(5000);
}
这只需要为每个测试调用一次,因此将其放在setUpPage()
中是一个好地方。在抱怨它无法找到给定元素之前,它会导致Selenium继续寻找最多五秒钟。页面完成加载和可以访问的元素需要五秒钟。
第一个问题(等到浏览器导航离开当前页面)需要对测试代码进行一些更改才能工作。首先,我们创建一个名为waitForURLChange()
的新函数,然后在下面的示例中,只要我们希望浏览器导航到新的URL,我们就会调用它。
private $urlPrevious;
/// Wait for the browser URL to change. Must be called once before the test starts
/// to read in the initial URL.
public function waitForURLChange()
{
if (!empty($this->urlPrevious)) {
$prev = $this->urlPrevious; // workaround for PHP limitation with $this and closures
$this->waitUntil(
function($testCase) use ($prev) {
return strcmp($testCase->url(), $prev) ? 1 : null;
},
2000 // milliseconds to wait before giving up if the URL hasn't changed
);
}
// Make sure the URL has actually changed (in case of timeout above)
$this->assertNotEquals($this->urlPrevious, $this->url());
$this->urlPrevious = $this->url();
}
现在,在您的测试代码中,您只需在导致页面转换的每个操作后调用waitForURLChange()
:
public function myTest()
{
// Call the function once first to read in the current URL. This does not wait. You
// could put this in setUpPage() instead so you don't need it at the start of every test.
$this->waitForURLChange();
// Do something that causes the browser to move to a different page.
$this->clickOnElement('somelink');
// Call the function again, and it won't return until the browser has started
// loading the new page.
$this->waitForURLChange();
// Now perform the test, knowing the page may not have fully loaded yet but you are
// definitely not stuck on the previous page.
$this->assertEquals('content', $this->byId('newElement'));
// Click the back button in the browser (for example).
$this->back();
// Wait until we're back at the previous page.
$this->waitForURLChange();
// ...and so on...
}