【问题标题】:PHPUnit + Selenium: How to handle moving between pagesPHPUnit + Selenium:如何处理页面之间的移动
【发布时间】:2014-05-21 10:26:19
【问题描述】:

一旦我的测试尝试从一页移动到另一页,我真的很难弄清楚如何阻止 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() 尝试从元素中获取值。但是新页面现在已经加载,我们拥有的元素引用来自 previous 页面。由于该元素不再存在于当前 DOM 中,因此您会收到有关过时元素的错误。

  2. clickOnElement() 开始加载下一页,但在加载完成之前byId()新的但未完全加载的页面上运行。由于页面尚未完成加载,您会收到“未找到元素”错误。

任何想法如何解决这两个问题?

【问题讨论】:

  • 我可以问一个明显的问题:您确定页面上确实存在“somelink”吗?您的代码示例看起来应该在正常情况下工作
  • 还可以尝试分解为 clickOnElement 快捷方式:$this->element($this->using('id')->value($id))->click();,但要分阶段进行并首先测试元素是否存在。
  • 它确实存在于页面上,因为某些测试会在前几次失败但后来成功,即某处存在竞争条件。但是像这样的测试总是失败。此外,如果页面上不存在该元素,我会收到“找不到元素”错误而不是上面的错误,所以我相信这不是问题的原因。
  • body onload 触发时,原始页面源中的元素是否由后端提供?还是以后由 JavaScript 动态生成?还是任何其他的 ansyc 进程,例如 Api 回调?
  • 重新缺少文档,见this question and my article referenced

标签: php selenium phpunit


【解决方案1】:

好的,我想我想通了。第二个问题(在页面加载之前寻找元素)可以通过设置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 在抱怨找不到给定元素之前继续查找长达 5 秒钟。 5 秒应该是足够的时间来完成页面加载和元素变得可访问。

第一个问题(等到浏览器离开当前页面)需要对测试代码进行一些更改才能正常工作。首先,我们创建一个名为 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...
}

【讨论】:

    猜你喜欢
    • 2014-02-23
    • 1970-01-01
    • 2020-11-05
    • 2019-06-09
    • 1970-01-01
    • 2018-04-22
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    相关资源
    最近更新 更多