全栈测试开发系列----WebDriver API及对象识别技术(一)

发布时间 2023-06-27 19:26:03作者: C_N_Candy

前言:  

  WebDriver API相比于之前的selenium-RC API而言,不仅解决了一些相关的限制,还使得接口更加简洁,同时更好的支持了页面本身不重新加载而页面元素发生变化的动态网页,所以WebDriver API的实现目的不仅是提供一个良好的面向对象API,而且对Web应用程序测试过程中所产生的问题也提供了很大的支持。

  Web应用程序的测试主要是基于调用WebDriver API来模拟用户的操作,然后判断操作结果是否与预期一致,从而实现自动化的目的。所以熟悉WebDriver API的使用显得至关重要,这也是完成自动化测试的先决条件。

Selenium WebDriver与RC的差异 - 知乎 (zhihu.com)

目录

  一、鼠标键盘模块

    1.1、鼠标事件(ActionChains)

    1.2、键盘事件(Keys)

  二、多个iframe处理

  三、下拉列表框的多种实现方式

  

 

一、鼠标键盘模块

1.1、鼠标事件

 

  API方法都封装在ActionChains类中。

from selenium.webdriver.common.action_chains import ActionChains

  

鼠标事件常用的API方法:

方法 说明
click(on_element=None)
单击鼠标左键
click_and_hold(on_element=None)
点击鼠标左键,不松开
context_click(on_element=None)
点击鼠标右键
double_click(on_element=None)
双击鼠标左键
drag_and_drop(source, target)
拖拽到某个元素然后松开
drag_and_drop_by_offset(source, xoffset, yoffset)
拖拽到某个坐标然后松开
key_down(value, element=None)
按下某个键盘上的键
key_up(value, element=None)
松开按键
move_by_offset(xoffset, yoffset)
鼠标从当前位置移动到某个坐标
move_to_element(to_element)
鼠标移动到某个元素
move_to_element_with_offset(to_element, xoffset, yoffset)
移动到距某个元素(左上角坐标)多少距离的位置
release(on_element=None)
在某个元素位置松开鼠标左键
send_keys(*keys_to_send)
发送某个键到当前焦点的元素
send_keys_to_element(element, *keys_to_send)
发送某个键到指定元素
perform()
执行链中的所有动作

 

注意:

在引用鼠标模块时,容易出现两个错误:

  1.ActionChains(驱动器对象)容易将驱动器对象漏掉

  2.在调用相关操作之后,没有添加执行步骤,即perfrom()

(ActionChains的执行原理,当你调用ActionChains的方法时,不会立即执行,而是会将所有的操作按顺序存放在一个队列里,当你调用perform()方法时,队列中的时间会依次执行)

 

这种情况下,又两种写法:(两种写法本质是一样的,ActionChains都会按照顺序执行所有的操作)

# 链式写法

menu = driver.find_element_by_css_selector(".nav")
submenu = driver.find_element_by_css_selector(".nav #submenu1")
ActionChains(driver).move_to_element(menu).click(submenu).perform()
# 分步写法

menu = driver.find_element_by_css_selector(".nav")
submenu = driver.find_element_by_css_selector(".nav #submenu1")
actions = ActionChains(driver)
actions.move_to_element(menu)
actions.click(submenu)
actions.perform()

 

1.1.1、点击操作

示例网址:http://sahitest.com/demo/clicks.htm

代码示例:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep
driver = webdriver.Firefox()
driver.implicitly_wait(10)  # 隐式等待10S
driver.maximize_window()    # 最大化窗口
driver.get('http://sahitest.com/demo/clicks.htm')
click_btn = driver.find_element_by_xpath('//input[@value="click me"]')  # 单击按钮
doubleclick_btn = driver.find_element_by_xpath('//input[@value="dbl click me"]')  # 双击按钮
rightclick_btn = driver.find_element_by_xpath('//input[@value="right click me"]')  # 右键单击按钮
ActionChains(driver).click(click_btn).double_click(doubleclick_btn).context_click(rightclick_btn).perform()  # 链式用法
print driver.find_element_by_name('t2').get_attribute('value')
sleep(2)
driver.quit()

 

1.1.2、鼠标移动

示例网址:http://sahitest.com/demo/mouseover.htm

代码示例:

# -*- coding: utf-8 -*-​
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep
​
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.maximize_window()
driver.get('http://sahitest.com/demo/mouseover.htm')
​
write = driver.find_element_by_xpath('//input[@value="Write on hover"]')  # 鼠标移动到此元素,在下面的input框中会显示“Mouse moved”
blank = driver.find_element_by_xpath('//input[@value="Blank on hover"]')  # 鼠标移动到此元素,会清空下面input框中的内容
​
result = driver.find_element_by_name('t1')
​
action = ActionChains(driver)
action.move_to_element(write).perform()  # 移动到write,显示“Mouse moved”
print result.get_attribute('value')
​
# action.move_to_element(blank).perform()
action.move_by_offset(10, 50).perform()  # 移动到距离当前位置(10,50)的点,与上句效果相同,移动到blank上,清空
print result.get_attribute('value')
​
action.move_to_element_with_offset(blank, 10, -40).perform()  # 移动到距离blank元素(10,-40)的点,可移动到write上
print result.get_attribute('value')
​
sleep(2)
driver.quit()

 

1.1.3、拖拽

示例网址:http://sahitest.com/demo/dragDropMooTools.htm

代码示例:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep
​
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.maximize_window()
driver.get('http://sahitest.com/demo/dragDropMooTools.htm')
​
dragger = driver.find_element_by_id('dragger')  # 被拖拽元素
item1 = driver.find_element_by_xpath('//div[text()="Item 1"]')  # 目标元素1
item2 = driver.find_element_by_xpath('//div[text()="Item 2"]')  # 目标2
item3 = driver.find_element_by_xpath('//div[text()="Item 3"]')  # 目标3
item4 = driver.find_element_by_xpath('//div[text()="Item 4"]')  # 目标4
​
action = ActionChains(driver)
action.drag_and_drop(dragger, item1).perform()  # 1.移动dragger到目标1
sleep(2)
action.click_and_hold(dragger).release(item2).perform()  # 2.效果与上句相同,也能起到移动效果
sleep(2)
action.click_and_hold(dragger).move_to_element(item3).release().perform()  # 3.效果与上两句相同,也能起到移动的效果
sleep(2)
# action.drag_and_drop_by_offset(dragger, 400, 150).perform()  # 4.移动到指定坐标
action.click_and_hold(dragger).move_by_offset(400, 150).release().perform()  # 5.与上一句相同,移动到指定坐标
sleep(2)
driver.quit()

一般用坐标定位很少,用上例中的方法1足够了,如果看源码,会发现方法2其实就是方法1中的drag_and_drop()的实现。注意:拖拽使用时注意加等待时间,有时会因为速度太快而失败。

 

 1.1.4、按键

   模拟按键有多种方法,能用win32api来实现,能用SendKeys来实现,也可以用selenium的WebElement对象的send_keys()方法来实现,这里ActionChains类也提供了几个模拟按键的方法。

 示例网址:http://sahitest.com/demo/keypress.htm

 代码示例1:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep
​
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.maximize_window()
driver.get('http://sahitest.com/demo/keypress.htm')
​
key_up_radio = driver.find_element_by_id('r1')  # 监测按键升起
key_down_radio = driver.find_element_by_id('r2')  # 监测按键按下
key_press_radio = driver.find_element_by_id('r3')  # 监测按键按下升起
​
enter = driver.find_elements_by_xpath('//form[@name="f1"]/input')[1]  # 输入框
result = driver.find_elements_by_xpath('//form[@name="f1"]/input')[0]  # 监测结果
# 监测key_down
key_down_radio.click()
ActionChains(driver).key_down(Keys.CONTROL, enter).key_up(Keys.CONTROL).perform()
print result.get_attribute('value')
​
# 监测key_up
key_up_radio.click()
enter.click()
ActionChains(driver).key_down(Keys.SHIFT).key_up(Keys.SHIFT).perform()
print result.get_attribute('value')
​
# 监测key_press
key_press_radio.click()
enter.click()
ActionChains(driver).send_keys('a').perform()
print result.get_attribute('value')
driver.quit()

 

示例网址:http://sahitest.com/demo/label.htm

键鼠组合操作,示例代码2:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from time import sleep
​
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.maximize_window()
​
driver.get('http://sahitest.com/demo/label.htm')
​
input1 = driver.find_elements_by_tag_name('input')[3]
input2 = driver.find_elements_by_tag_name('input')[4]
​
action = ActionChains(driver)
input1.click()
action.send_keys('Test Keys').perform()
action.key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()  # ctrl+a
action.key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()  # ctrl+c
​
action.key_down(Keys.CONTROL, input2).send_keys('v').key_up(Keys.CONTROL).perform()  # ctrl+v
print input1.get_attribute('value')
print input2.get_attribute('value')
driver.quit()

复制粘贴用WebElement< input >.send_keys()也能实现,大家可以试一下,也可以用更底层的方法,同时也是os弹框的处理办法之一的win32api,有兴趣也可以试试SendKeys、keybd_event

 

以上代码出处:

作者: liangxb

出处:https://www.cnblogs.com/lxbmaomao/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

 

1.2、键盘事件

   设置键盘事件,其中键盘事件相关的API方法封装在Keys类中

from selenium.webdriver.common.keys import Keys

 

 1.2.1、删除事件

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get('http://192.168.235.128/index.php/home/login/login.html')
driver.maximize_window()
driver.implicitly_wait(20)

driver.find_element(By.ID,"member_name").send_keys("test")  # 输入用户名
time.sleep(5)
driver.find_element(By.ID,"member_name").send_keys(Keys.BACKSPACE)  # 删除

 

 1.2.2、空格事件

driver.find_element(By.ID,"member_name").send_keys(Keys.SPACE)  # 输入空格
time.sleep(5)
driver.find_element(By.ID,"member_name").send_keys("test")  # 输入用户名

 

1.2.3、全选事件

driver.find_element(By.ID,"member_name").send_keys("test") # 输入用户名
time.sleep(5)
driver.find_element(By.ID,"member_name").send_keys(Keys.CONTROL,'a')    # 全选

 

1.2.4、复制事件、粘贴事件

driver.find_element(By.ID,"member_name").send_keys("test")  # 输入用户名
driver.find_element(By.ID,"member_name").send_keys(Keys.CONTROL,'c') # 复制
driver.find_element(By.ID,"member_pass").send_keys(Keys.CONTROL,'v') # 粘贴
driver.find_element(By.CLASS_NAME,"button").click() # 点击登录

 

 键盘事件可以使用上、下键乘以随机产生的数来完成向上或者向下的选择,具体实现代码如下:

driver.find_element(By.NAME,"menber_pass").send_keys(Keys.ARROW_DOWN*3) # 向下移动3

 

 

二、多个iframe处理

  iframe是HTML中的框架标签,表示文档中可以嵌入文档,或者说是浮动的框架。在selenium中iframe同样如此,如果驱动器对象处于当前iframe框架中,此时驱动器对象是无法操作其他的iframe的。如果需要操作,则需要调用对应的API方法完成iframe的切换操作。

  iframe标签有frameset、frame、iframe三种类型,其中frameset跟其他普通标签没有区别,不会影响到正常的定位,frame和iframe对selenium定位而言是一样的。selenium有一组方法对frame进行操作。

 

 2.1、如何切换frame

  selenium提供了switch_to.frame()方法来切换frame。

switch_to.frame(param)

param表示传入的参数,该参数可以是iframe元素的id、name、index等属性,还可以是定位该iframe所返回的selenium的WebElement对象。

 HTML代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>iframe测试</title>
</head>
<body>
<iframe src="iframe_test.html" id="iframe1" name="myiframe"></iframe>
</body>
</html>

效果图

 

想要定位并切换其中的iframe,可以通过以下代码实现

代码示例:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()

# 用frame的索引index来定位,第一个是0
driver.switch_to.frame(0)
# 可以使用id来定位
driver.switch_to.frame("iframe1")
# 可以使用name来定位
driver.switch_to.frame("myframe")
# 使用WebElement对象来定位
driver.switch_to.frame(driver.find_element(By.ID, ("iframe1")))

 

   一般情况下,如果iframe存在id和name属性,则使用这两个属性可以直接定位但有些场景中前段frame框架中不一定设置这两个属性,name就需要使用index和WebElement来完成定位操作

    (1)index的值是从0开始的,传入整形参数即表示使用index来进行定位,但是如果传入的是String类型,则表示使用id、name来进行定位。

    (2)WebElement对象,即使用find_element系列方法所取得的对象可以使用tag_name、XPath、父子定位等方式来定位frame对象

示例:

<iframe src="iframe_test.html" ></iframe>

用XPath定位,传入WebElement对象

driver.switch_to.frame(driver.find_element(By.XPATH, ("//iframe(contains(@src,'test'))")))

 

 

2.2、从frame中切换回主文档

  如果切换到frame中,则不能继续操作主文档中的元素。如果需要操作主文档元素,则必须完成文档与frame的切换操作。

# 切换回主文档
driver.switch_to.default_content()

 

 2.3\嵌套frame的操作

  有时候会遇到多个frame嵌套的情况,示例(其实这个写法是不支持的,做简单演示使用)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>iframe测试</title>
</head>
<body>
<iframe id="iframe1">
    <iframe id="iframe2"/>
</iframe>
</body>
</html>

(1)先从主文档切换到iframe1,再从iframe1切换到iframe2,一层一层切换进去

driver.switch_to.frame("iframe1")
driver.switch_to.frame("iframe2")

(2)从iframe2再切换回iframe1,这里selenium提供了一个方法能从子iframe切回父iframe,而不用先切回主文档,再切换回来

driver.switch_to.parent_frame()

    有了parent_frame()这个相当于后退的方法,就可以随意切换不同的frame3

 

 

2.3、下拉类表框的多种处理方式

  在一个网页中,除了前面遇到的一些控件类型以外,还经常会遇到下拉列表框。对下拉列表框的操作,有时需要click两次才能完成选择,这种操作方式很容易出错。实际上selenium也提供了专门的select模块(即下拉列表处理模块)。

  2.3.1、间接选择

    先定位到下拉列表框,再定位其中的选项,具体实现代码如下。(书中的商城已经没有了新增客户选项)

 

# 浏览器驱动对象创建:Base_Class.py

from selenium import webdriver


class BaseClass(object):
    # 需要浏览器驱动
    def __init__(self, url, browserType):
        if browserType == webdriver.Firefox():
            self.driver = webdriver.Firefox()
        elif browserType == webdriver.Chrome():
            self.driver = webdriver.Chrome()
        self.driver.get(url)
        self.driver.maximize_window()

        # 设置隐式等待时间(全局有效,只需要设置一次)
        self.driver.implicitly_wait(30)
# 登录类:Login_Class.py

from Base_Class import BaseClass
from selenium.webdriver.common.by import By

# 继承类
class LoginClass(BaseClass): def __init__(self, url, browserType): super().__init__(url, browserType) # 这里没有做验证正码的处理,默认输入账号密码登录,使用了基础定位、JS定位、jQuery定位 def login(self, username, password): # 用户名定位 username_js = """return document.querySelector("[name='username']").value='%s';""" % username # 执行脚本 print(self.driver.execute_script(username_js)) self.driver.find_element(By.XPATH,"//input[@name='userword']").send_keys(password) # 点击登录 self.driver.execute_script("$('.logindo').click()")
from BaseMoudule.Login_Class import LoginClass
from selenium.webdriver.common.by import By
import time


# 继承登录类
class AddCustomer(LoginClass):
    def __init__(self, url, username, password):
        super(AddCustomer, self).__init__(url)
        self.username = username
        self.password = password

    # 完成添加客户的单击操作,前置条件:处于登录状态
    def click_add_customer(self):
        self.login(self.username, self.password)
        # 单击客户管理
        click_first = "$('li:first').click()"
        self.driver.execute_script(click_first)
        time.sleep(4)
        # 切换iframe
        # self.driver.switch_to.frame("open11")
        # 没有id,name,可以传入定位iframe对象,切换到客户管理页面
        self.driver.switch_to.frame(
            self.driver.find_element(By.XPATH, "//div[@class='aui_contenttaui_state_full']/iframe"))
        self.driver.find_element(By.XPATH, "//a[@data-title='新增客户']").click()
        time.sleep(4)

        # 切回主文档
        self.driver.switch_to.default_content()
        # 切换iframe,切换到新增客户页面
        self.driver.switch_to.frame("openopen11")
        time.sleep(2)

        # 先定位到select对象
        get_select = self.driver.find_element(By.XPATH, "//*[id='form1']/div[1]/dl[3]/dd/select")
        # 再定位option
        get_select.find_element(By.CSS_SELECTOR,"option[value='A客户']").click()

if __name__ == '__main__':
    add = AddCustomer('http://192.168.235.128/','admin','123456')
    add.click_add_customer()

 

题外扩展:关于Python中的继承、多继承:包含super(子类, self).__init__()以及其他父类的方法

Python类的继承关系及super的用法 - 知乎 (zhihu.com)

 Python类继承及super()函数_waifdzdn的博客-CSDN博客

 Python类中的继承——super(包含super(子类, self).__init__()以及其他父类的方法)_python 继承 super_新池坡南的博客-CSDN博客

 

   2.3.2、直接选择

    直接定位到下拉选项框中的选项,可以通过以下代码表示,

self.driver.find_element(By.XPATH, "//*[id='form']/div[1]/dl[3]/dd/select/option2").click()

 

  2.3.3、select模块

     WebDriver API中内置了一个Select模块,该模块专门用于实现下拉表框的处理操作,使用时导入即可。

from selenium.webdriver.support.select import Select

    其中三种定位选项的方法:

      1.select_by_index():通过索引定位(从0开始)

      2.select_by_value():通过value值定位

      3.select_by_visible_text():通过选项的文本值定位

# 获取Select对象
select_object = Select(self.driver.find_element(By.XPATH,"//*[@id='frome1']/div[1]/dl[3]/dd/select")) 
# 通过索引获取
select_object.select_by_index(2)
# 通过value值获取
select_object.select_by_value("D客户")
# 通过文本值获取
select_object.select_by_visible_text("B客户")

 

  2.3.4、Select模块提供了四种取消选中项的方法

  1. deselect_all:取消全部的已选项
  2. deselect_by_index():取消已选中的索引项
  3. deselect_by_value():取消已选中的value值
  4. deselect_by_visible_text():取消已选中的文本值

 在日常的WEB测试中,经常会遇到某些下拉列表选项已经被默认选中的情况,这时就需要用到这四种取消选中项的方法