解决Selenium元素拖拽不生效Bug

前几天在使用Selenium进行元素拖拽操作时,发现Selenium自带的元素拖拽方法(dragAndDrop())不生效,网上的回答也是五花八门,比较混乱,尝试了以下几种方法均无法解决。

方案1:通过dragAndDrop()方法将元素拖放到特定区域上------无效。

  1.   // 要拖拽的元素

  2.   WebElement draggable = driver.findElement(By.xpath(""));

  3.   // 目标元素/区域

  4.   WebElement droppable = driver.findElement(By.xpath(""));

  5.   new Actions(driver).dragAndDrop(draggable, droppable).build().perform();

方案2:通过dragAndDropBy()方法将元素进行指定像素位移,从而实现拖放到特定区域,该方法需要先找到元素的像素------无效。

复制代码
new Actions(driver).dragAndDropBy(draggable,135, 40).build().perform();

方案3:先通过clickAndHold()方法点击并按住元素,然后使用moveByOffset()方法将元素拖拽到目标区域,再使用release()方法将按住的元素释放------无效。

复制代码
new Actions(driver).clickAndHold(draggable).moveByOffset(400, 0).release().build().perform();

方案4:先通过clickAndHold()方法点击并按住元素,然后使用moveToElement()方法将元素拖拽到指定元素上,再使用release()方法将元素释放------无效。

  1.   new

  2.   Actions(driver).clickAndHold(draggable).moveToElement(droppable).release(droppable).build().perform();

方案5:借助Robot类实现拖拽------无效。

  1.   Point coordinates1 = draggable.getLocation();

  2.   Point coordinates2 = droppable.getLocation();

  3.   Robot robot = new Robot();

  4.   robot.mouseMove(coordinates1.getX(), coordinates1.getY());

  5.   robot.mousePress(InputEvent.BUTTON1_MASK);

  6.   robot.mouseMove(coordinates2.getX(), coordinates2.getY());

  7.   robot.mouseRelease(InputEvent.BUTTON1_MASK);

  8.   ......

以上方案均未生效,具体表现为运行均无任何报错,但在应用程序中未发生拖放。

经过一顿操作,最终在「Selenium Drag and Drop Bug Workaround」上找到了问题原因及解决方案。

经了解,Selenium的拖放功能在某些情况下无效的错误已经存在多年。

原因是拖放功能包含三个动作:单击并按住(click and hold)、将鼠标移动到其他元素或位置(move mouse to other element/location)、释放鼠标(release mouse),问题在于最后一步释放鼠标的操作,当Webdriver API发送释放鼠标的请求时,在某些情况下它会一直按住它,所以导致拖放功能无效。

解决方法就是通过Webdriver API将JavaScript代码发送到浏览器,利用JavaScript模拟拖放操作,而不使用Webdriver自带的拖放方法。

其工作原理是将浏览器实例和CSS选择器找到的两个Web元素作为参数,然后在浏览器端执行JavaScript代码。

如果你是使用Python+Selenium技术栈实现的Web UI自动化,可以直接下载seletools(Selenium Tools,作者:Dmitrii Bormotov)包,并将它导入到需要执行拖放的地方,然后简单地调用它的drag_and_drop()方法即可。

  1.   pip install seletools

  2.   from seletools.actions import drag_and_drop

  3.   source = driver.find_element(By.CSS_SELECTOR, "#column-a")

  4.   target = browser.find_element(By.CSS_SELECTOR, "#column-b")

  5.   drag_and_drop(driver, source, target)

如果使用的是Java+Selenium技术栈,则可以使用以下代码实现:

  1.   // 要拖拽的元素

  2.   WebElement draggable = driver.findElement(By.xpath(""));

  3.   // 目标元素

  4.   WebElement droppable = driver.findElement(By.xpath(""));

  5.   // 拖动前先点击并按住要拖拽的元素,避免在elementui,拖放前draggable属性才会变成true,目的是让draggable变成true,如果一开始就是true也可不加这句

  6.   new Actions(driver).clickAndHold(draggable).perform();

  7.   final String java_script = "var args = arguments," + "callback = args[args.length - 1]," + "source = args[0]," + "target = args[1]," + "offsetX = (args.length > 2 && args[2]) || 0," + "offsetY = (args.length > 3 && args[3]) || 0," + "delay = (args.length > 4 && args[4]) || 1;" + "if (!source.draggable) throw new Error('Source element is not draggable.');" + "var doc = source.ownerDocument," + "win = doc.defaultView," + "rect1 = source.getBoundingClientRect()," + "rect2 = target ? target.getBoundingClientRect() : rect1," + "x = rect1.left + (rect1.width >> 1)," + "y = rect1.top + (rect1.height >> 1)," + "x2 = rect2.left + (rect2.width >> 1) + offsetX," + "y2 = rect2.top + (rect2.height >> 1) + offsetY," + "dataTransfer = Object.create(Object.prototype, {" + " _items: { value: { } }," + " effectAllowed: { value: 'all', writable: true }," + " dropEffect: { value: 'move', writable: true }," + " files: { get: function () { return undefined } }," + " types: { get: function () { return Object.keys(this._items) } }," + " setData: { value: function (format, data) { this._items[format] = data } }," + " getData: { value: function (format) { return this._items[format] } }," + " clearData: { value: function (format) { delete this._items[format] } }," + " setDragImage: { value: function () { } }" + "});" + "target = doc.elementFromPoint(x2, y2);" + "if(!target) throw new Error('The target element is not interactable and need to be scrolled into the view.');" + "rect2 = target.getBoundingClientRect();" + "emit(source, 'dragstart', delay, function () {" + "var rect3 = target.getBoundingClientRect();" + "x = rect3.left + x2 - rect2.left;" + "y = rect3.top + y2 - rect2.top;" + "emit(target, 'dragenter', 1, function () {" + " emit(target, 'dragover', delay, function () {" + "\ttarget = doc.elementFromPoint(x, y);" + "\temit(target, 'drop', 1, function () {" + "\t emit(source, 'dragend', 1, callback);" + "});});});});" + "function emit(element, type, delay, callback) {" + "var event = doc.createEvent('DragEvent');" + "event.initMouseEvent(type, true, true, win, 0, 0, 0, x, y, false, false, false, false, 0, null);" + "Object.defineProperty(event, 'dataTransfer', { get: function () { return dataTransfer } });" + "element.dispatchEvent(event);" + "win.setTimeout(callback, delay);" + "}";

  8.   // 默认拖拽到中心点位置,第3个参数是X坐标偏移量(左负右正),第4个参数为Y坐标偏移量(上负下正),第5个参数是延迟时间(单位为毫秒,表示当鼠标点下后,延迟指定时间后才开始激活拖拽动作,用来防止误点击)

  9.   ((JavascriptExecutor) driver).executeScript(java_script, draggable, droppable, -200, -300, 500);

以上就是在Python和Java中的解决方案,至于为什么不在Selenium中直接修改程序,而是创建单独的包来处理,以下是Dmitrii Bormotov的说法:

复制代码
The drag and drop bug is a webdriver issue, so all you can do on the Selenium side is to simply perform the same workaround that I did. I spoke with David Burnes (core Selenium committer) about pushing that workaround into Selenium, but he said that it is not a good idea to have any workarounds in Selenium itself. That is why I had to create a separate package to help the test automation community with this problem.

大概的意思就是拖放错误是一个webdriver网络驱动问题,David Burnes(核心 Selenium 提交者)认为在Selenium中提供任何暂时避开网络的方法并不是一个好主意。

相关推荐
海姐软件测试2 小时前
接口和压测工具都有哪些,是如何选择的?
测试工具·面试·职场和发展
天才测试猿2 小时前
Python接口自动化浅析unittest单元测试原理
自动化测试·软件测试·python·测试工具·单元测试·测试用例·集成测试
二次觉醒7 小时前
Postman接口调用传参说明
测试工具·lua·postman
网络安全小吗喽9 小时前
靶场(十三)---小白心得&&思路分享---Levram
linux·运维·服务器·测试工具·安全·网络安全
带娃的IT创业者9 小时前
《Python实战进阶》No23: 使用 Selenium 自动化浏览器操作
python·selenium·自动化
做测试的小薄10 小时前
当 Selenium 的 click() /send_keys()等方法失效时:JavaScript 在 UI 自动化测试中的神奇用法
javascript·自动化测试·selenium·ui
艰默15 小时前
Bug 算法路径规划:原理、推导与实现
人工智能·算法·bug·路径规划
星光不负赶路人!1 天前
【工作记录】F12查看接口信息及postman中使用
测试工具
go_to_hacker2 天前
奇安信面试题
测试工具·安全·web安全·渗透测试
one day3212 天前
Selenium 自动化测试学习总结
学习·selenium·测试工具