作者:测试蔡坨坨
你好,我是测试蔡坨坨。
以下几种方法均无法解决。
// 要拖拽的元素
WebElement draggable = driver.findElement(By.xpath("";
// 目标元素/区域
WebElement droppable = driver.findElement(By.xpath("";
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(方法将元素释放——无效
new Actions(driver.clickAndHold(draggable.moveToElement(droppable.release(droppable.build(.perform(;
方案5:借助Robot类实现拖拽——无效
Point coordinates1 = draggable.getLocation(;
Point coordinates2 = droppable.getLocation(;
Robot robot = new Robot(;
robot.mouseMove(coordinates1.getX(, coordinates1.getY(;
robot.mousePress(InputEvent.BUTTON1_MASK;
robot.mouseMove(coordinates2.getX(, coordinates2.getY(;
robot.mouseRelease(InputEvent.BUTTON1_MASK;
……
经过一顿操作,最终在「Selenium Drag and Drop Bug Workaround」上找到了问题原因及解决方案。
原因是拖放功能包含三个动作:单击并按住(click and hold)、将鼠标移动到其他元素或位置(move mouse to other element/location)、释放鼠标(release mouse),问题在于最后一步释放鼠标的操作,当Webdriver API发送释放鼠标的请求时,在某些情况下它会一直按住它,所以导致拖放功能无效。
其工作原理是将浏览器实例和CSS选择器找到的两个Web元素作为参数,然后在浏览器端执行JavaScript代码。
pip install seletools
from seletools.actions import drag_and_drop
source = driver.find_element(By.CSS_SELECTOR, "#column-a"
target = browser.find_element(By.CSS_SELECTOR, "#column-b"
drag_and_drop(driver, source, target
如果使用的是Java+Selenium技术栈,则可以使用以下代码实现:
// 要拖拽的元素
WebElement draggable = driver.findElement(By.xpath("";
// 目标元素
WebElement droppable = driver.findElement(By.xpath("";
// 拖动前先点击并按住要拖拽的元素,避免在elementui,拖放前draggable属性才会变成true,目的是让draggable变成true,如果一开始就是true也可不加这句
new Actions(driver.clickAndHold(draggable.perform(;
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;" + "}";
// 默认拖拽到中心点位置,第3个参数是X坐标偏移量(左负右正),第4个参数为Y坐标偏移量(上负下正),第5个参数是延迟时间(单位为毫秒,表示当鼠标点下后,延迟指定时间后才开始激活拖拽动作,用来防止误点击)
((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中提供任何暂时避开网络的方法并不是一个好主意。