PHPUnit + Selenium:如何处理页面之间的移动

时间:2014-05-21 10:26:20

标签: php selenium phpunit

一旦我的测试试图从一个页面移动到另一个页面,我真的很难弄清楚如何阻止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

问题仅在两个页面之间移动时出现,并且您要查找的元素存在于两个页面上(如标题或标题。)这里实际上存在两个问题,这两个问题都是竞争条件:

  1. clickOnElement()开始下一页加载,但在此之前,byId()当前页上运行并找到该元素。然后浏览器完成加载新页面,assertContains()尝试从元素中获取值。但是现在已经加载了新页面,我们的元素引用来自之前的页面。因为当前DOM中不再存在该元素,所以您会收到有关陈旧元素的错误。

  2. clickOnElement()启动下一页加载,但在完成加载byId()之前,新的,但未完全加载的页面上运行。由于页面尚未完成加载,因此会出现“未找到元素”错误。

  3. 如何解决这两个问题?

1 个答案:

答案 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...
}