当前位置: 首页 > 知识库问答 >
问题:

与元素交互抛出过时元素引用

翟理
2023-03-14

我有这个网站: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)
(驱动程序信息:chronidriver=2.15.322455(ae8db840dac8d0c453355d3d922c91adfb61df8f),platform=Mac OS X 10.10.3 x86_64)(警告:服务器未提供任何堆栈跟踪信息)命令持续时间或超时:9毫秒有关此错误的文档,请访问:http://seleniumhq.org/exceptions/stale_element_reference.html内部版本信息:版本:“2.45.0”,修订版:“5017cb8”,时间:“2015-02-26 23:59:50”系统信息:主机:“ITs MacBook Pro”。本地',ip:'129.192.20.150',os。name:'Mac OS X',操作系统。arch:'x86_64',操作系统。版本:“10.10.3”,java。版本:“1.8.0_31”驱动程序信息:org.openqa.selenium.chrome。ChromeDriver功能[{applicationCacheEnabled=false,rotatable=false;mobileEmulationEnabled=false,chrome={userDataDir=/var/folders/rr/63848xd90yscgwpkfn8srbyh0000gq/T/.org.chrominal.chromium.rarNyX},takesHeapSnapshot=true,databaseEnabled=false,handlesAlerts=true、version=43.0.2357.81,platform=MAC,browserConnectionEnabled=false,nativeEvents=true,acceptSslCerts=true,locationContextEnabled=1true,webStorageEnabled=2true,browsersName=chrome,takessScreenshot=true、javascriptEnabled=3true,cssSelectorsEnabled=0.true}]会话ID:235ec977a69d98c7f5b75a329e8111b2

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

如果我注释掉这一行:element(by.buttonText('start'))。单击()规范成功。

共有2个答案

贾飞章
2023-03-14

浏览器与量角器测试异步运行。这个例子很好地强调了这一点(这是许多量角器测试的一个问题,但通常不那么明显)。这由看起来像一条线的东西组合而成:

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

实际上需要多次往返浏览器(返回并解决了很多promise:element.alllast>)。对于大多数稳定后“被动”的网页来说,这不是问题,但对于任何在没有用户输入的情况下动态更改的网页,使用量角器进行测试可能会很痛苦。

要在浏览器中进行“原子”查找和测试,从而避开这个问题,您可以定义一个在浏览器中运行的函数(我的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中的任何浏览器兼容性魔力(例如,如果< code>getText()不是简单的< code>innerText访问器,我不会感到惊讶)。

另外,请注意,您仍然有一个比赛条件。在“单击”开始操作和此代码实际检查DOM之间,DOM可能已发生变化,也可能未发生变化(“Name999”可能已不存在)。

严修诚
2023-03-14

我为此做了很多努力,试图弄明白为什么会发生这种情况。我首先认为,指向列表最后一个元素的元素查找器是在交互完成之前很久创建的,所以在创建元素查找器和交互之间的这段时间内,元素可以从DOM中分离出来,这并不奇怪。

我后来发现,元素是在交互完成之前找到的,每次你与某物交互时。因此,指向最后一个元素实际上应该指向与元素交互的时间的最后一个元素。

使用浏览器.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毫秒,那么出错的机会就会小得多。所以这取决于你的计算机在出现这个错误时运行的速度。

修复取决于你,但是有了这个信息,我希望该怎么做会更清楚一点。

 类似资料: