与元素交互抛出陈旧元素参考

时间:2015-06-05 16:41:36

标签: webdriver protractor

我有这个网站:http://embed.plnkr.co/Bs5iDqtXSSnvye2ORI6k/preview

代码:

var app = angular.module('plunker', []);

var a = new Array(1000);

for (var i = 0; i< 1000; i++) {
  a[i] = 'Name' + i;
}

app.controller('MainCtrl', function($scope, $interval) {
  $scope.names = a;

  $scope.start = function () {
    $interval(function () {
      $scope.names.pop();
    }, 50);
  }
});

以下规格:

'use strict';

describe('Name list', function () {
    it('should get the text of the last name', function () {
        browser.driver.get('http://embed.plnkr.co/Bs5iDqtXSSnvye2ORI6k/preview');
        browser.switchTo().frame(browser.driver.findElement(protractor.By.tagName('iframe')));
        element(by.buttonText('start')).click();
        expect(element.all(by.tagName('span)).last().getText()).toBe('Name999');
    });
});

这个配置:

'use strict';

// An example configuration file.
exports.config = {
    baseUrl: 'http://localhost:3000',
    seleniumAddress: 'http://localhost:4444/wd/hub',
    specs: ['stale.spec.js']
};

当我运行Protractor时,我收到以下错误:

  

StaleElementReferenceError:陈旧元素引用:元素不是   附在页面文档上(会话信息:chrome = 43.0.2357.81)
  (驱动信息:chromedriver = 2.15.322455   (ae8db840dac8d0c453355d3d922c91adfb61df8f),platform = Mac OS X 10.10.3   x86_64)(警告:服务器未提供任何堆栈跟踪   信息)命令持续时间或超时:9毫秒For   有关此错误的文档,请访问:   http://seleniumhq.org/exceptions/stale_element_reference.html建立   信息:版本:&#39; 2.45.0&#39;,修订版:&#39; 5017cb8&#39;,时间:&#39; 2015-02-26   23:59:50&#39;系统信息:主持人:&#39; ITs-MacBook-Pro.local&#39;,ip:   &#39; 129.192.20.150&#39;,os.name:&#39; Mac OS X&#39;,os.arch:&#39; x86_64&#39;,os.version:   &#39; 10.10.3&#39;,java.version:&#39; 1.8.0_31&#39;司机信息:   org.openqa.selenium.chrome.ChromeDriver功能   [{applicationCacheEnabled = false,rotate = false,   mobileEmulationEnabled =假,   铬= {userDataDir = /变种/文件夹/ RR / 63848xd90yscgwpkfn8srbyh0000gq / T / .org.chromium.Chromium.rarNyX},   takesHeapSnapshot = true,databaseEnabled = false,handlesAlerts = true,   version = 43.0.2357.81,platform = MAC,browserConnectionEnabled = false,   nativeEvents = true,acceptSslCerts = true,locationContextEnabled = true,   webStorageEnabled = true,browserName = chrome,takesScreenshot = true,   javascriptEnabled = true,cssSelectorsEnabled = true}]会话ID:   235ec977a69d98c7f5b75a329e8111b2

这意味着我尝试与之交互的元素(获取元素的文本)不再附加到DOM。这个例子真的是我的规范。在我的真实规范中真正发生的是我尝试获取元素列表的最后一个元素的文本(由ng-repeat生成)。还会发生的是,模型通过删除表示元素列表的数组的最后一个元素来更新。上面的例子只是重现错误(每次)。

如果我注释掉这一行:element(by.buttonText('start')).click();规范是成功的。

2 个答案:

答案 0 :(得分:6)

我为此苦苦挣扎,并试图弄清楚为什么会这样。我首先想到的是,指向列表最后一个元素的元素查找器是在交互完成之前很久就已经创建的,所以我毫不奇怪,元素可以在创建之间的那段时间内与DOM分离。元素查找器和交互。

我后来发现的是,每次与某些内容进行交互时,都会在交互完成之前找到该元素。因此,指向最后一个元素应该实际指向与元素交互的时间的最后一个元素。

使用browser.pause()我能够看到WebDriver真正做了什么,这是抛出错误之间的两个任务:

(pending) Task::414<WebDriver.findElements(By.tagName("span"))>
| | | | | | Task: WebDriver.findElements(By.tagName("span"))
| | | | | |     at Array.forEach (native)

这里介于两者之间,根据模型更新DOM,并且分离列表的最后一个元素。

(pending) Task::1170<WebElement.getText()>
| | | | | | Task: WebElement.getText()
| | | | | |     at Array.forEach (native)

在这个小小的执行漏洞中更新了DOM。目前,模型每50毫秒更新一次,这肯定会引发过时元素参考错误。但是如果我将间隔增加到1000毫秒,那么获得错误的机会就会少得多。因此,如果出现此错误,则取决于计算机的运行速度。

修复取决于你,但是我希望通过这些信息可以更清楚地做什么。

答案 1 :(得分:1)

浏览器与量角器测试异步运行。这个例子确实很好地突出了它(这对许多量角器测试来说是一个问题,但通常不那么明显)。这看起来像是一条线而复杂化了:

expect(element.all(by.tagName('span')).last().getText()).toBe('Name999');

实际上需要多次往返浏览器(有许多承诺被退回并解决:element.alllastgetText)。对于大多数已被稳定的“被动”网页,这不是问题,但对于任何没有用户输入而动态更改的网页,使用量角器进行测试可能会很痛苦。

要在浏览器中进行查找和测试“原子”,从而支持这个问题,你可以定义一个在浏览器中运行的函数(我的CSS DOM-fu很弱,所以希望有人可以调整它来制作它不太可怕):

expect(browser.executeScript(function() {
   var spans = document.getElementsByTagName('span');
   var lastSpan = spans[spans.length - 1]; // XXX handle empty spans
   return lastSpan.innerText;
}).toBe('Name999');

请注意,“功能”是在浏览器上序列化并执行的,因此封闭范围内的变量不起作用。此外,这种方法会丢失任何隐藏在量角器或webdriver中的浏览器兼容性魔法(例如,如果getText()不仅仅是innerText访问者,我也不会感到惊讶。

另外,请注意您仍有竞争条件。在“点击”以启动事件和此代码实际检查DOM之间,它可能已经或可能没有被变异(并且“Name999”可能已经消失)。