元素定位
一、直接调用WebDreiver的方法
driver.find_element_by_xpath
二、重写webdriver的find方法
def find_elements_xpath(self, *loc):
driver = self.driver
"""
定位元素,定位正确后返回元素的信息,外部调用传入元组参数必须有*,
例如:
find_element(*self.native_caixun)
:param loc: 元组类型,结构必须是(By.NAME, u'财讯')
:return: element
"""
try:
element = WebDriverWait(driver, 20, 0.2).until(lambda driver: driver.find_elements_by_xpath(*loc))
return element
except Exception as e:
print(current_time() + 'Error details :%s' % (e.args[0]))
一般通过xpath和id就能定位绝大多是元素
三、使用JS定位
某些元素在js里面,通过xpath不易定位,此时可以考虑使用JS去定位元素。注意Elements返回的数组,Element返回的是单个元素
js = 'document.getElementsByName("pm/projBuild")[0].click()'
# 使用下列方法执行js
self.driver.execute_script(js)
JS的document属性,定位元素的方法
document.getElementById()
# 返回指定id的元素
document.getElementsByName()
# 返回带有指定名称的对象的集合。注:返回一个数组
document.getElementsByTagName()
# 返回带有指定标签名的对象的集合。注:返回一个数组。
document.getElementsByClassName()
# 返回文档中所有指定类名的元素集合,作为 NodeList 对象:返回一个数组。
实现页面滚动
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
以上语句实现了拉动页面到底部的功能,其中window.scrollTo(左边距,上边距)是JavaScript中用于设置浏览器窗口滚动条的水平和垂直位置的代码。
text = "input text"
driver.execute_script("var obj=document.getElementById('text'); obj.value=' " + text + " ';")
以上代码实现了对id='text'的对话框(不能通过send_keys()输入文本)输入文本的操作
video = driver.find_element_by_xpath("body/Section[1]/div/video")
url = driver.execute_script("return arguments[0].currentSrc;", video) # url是文件播放地址
driver.execute_script("return arguments[0].play()", video) # 播放视频
sleep(15) # 播放15秒钟
driver.execute_script("arguments[0].pause()", video) #暂停视频
以上实现了HTML5视屏<video>标签的部分测试,更多内容参考HTML DOM Video对象。
其中arguments是JavaScript的内置对象。因为将video对象传给了arguments[0],所以arguments[0]相当于JavaScript脚本的document.getElementsByTagName("video")
。JavaScript不支持重载,使用arguments对象可以模拟函数重载效果。
四、页面还没有加载出来,就对页面上的元素进行的操作
因为加载元素延时造成的脚本失败,我们可以通过设置等待时间来提升自动化脚本的稳定性。
1:WebDriverWait() 显示等待。等待单个的元素加载,通常配合until()、until_not()方法使用。
WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)
"""
driver - 传入WebDriver实例,必填
timeout - 最长等待时间,必填
poll_frequency - 调用`until`/`until_not`方法的时间间隔,默认为0.5秒,可省。
ignored_exceptions - 忽略异常,默认仅包含NoSuchElementException,可省。
"""
WebDriverWait(driver,10).until(method,message='') '等待目标出现'
WebDriverWait(driver,5,1).until_not(method,message='') '等待目标消失'
"""
method - 必填。
message - 默认为空,可省。如果超时,抛出TimeoutException,返回message信息。
"""
# 如下:
WebDriverWait(driver, 超时时长, 调用频率, 忽略异常).until(可执行方法, 超时时返回的信息)
WebDriverWait(driver,5,1).until(expected_conditions.presence_of_element_located(By.ID,'kw'))
最长等待时间为5s,每隔1秒检查一次id='kw'的元素是否被加载在DOM树里(并不代表该元素一定可见)。最常用的method是expected_conditions类提供的预期条件判断。
is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).until_not(lambda x: x.find_element_by_id('someId').is_displayed())
最长等待时间为30s,每隔1秒检查一次id='someId'的元素是否从DOM树里消失,忽略默认异常信息8NoSuchElementException 和指定的异常信息ElementNotVisibleException*。此处匿名函数lambda
的用法具体参考Python语法。
2:driver.implicitly_wait(秒)
隐式等待。全局等待,对所有元素设置超时时间,等待页面的加载,因此只需要设置一次即可。这里的时间是最长等待时间(非固定等待时间)。
3:sleep(秒)
线程等待。休眠固定的时间,使用时需要先引入time模块的sleep方法from time import sleep
。
五、元素被遮挡,不可用,不可见
1:driver.maximize_window()
由于窗口大小改变引起的页面元素布局发生变化,被测元素被遮挡,可以先将窗口最大化,再进行元素定位。
2:.is_enabled()
由于业务原因元素在某些情况下不可用(元素属性disabled,灰显),首先检查测试步骤是否符合业务逻辑,其次确认是否为业务流程上的Bug。
3:.is_displayed()
对于属性不一定可见的元素,在定位前首先判断其属性是否可见,是否被隐藏。
4:由于布局不合理导致的元素被遮盖、或是元素本身缺失引起的无法定位问题属于Bug,可以提给开发让其改进。
六、WebDriver无法操作Windows控件
1.使用AutoIt定位控件,编写脚本,编译为exe,然后调用python执行os.system("D:\\upfile.exe")
2.使用win32模块调用windwos的API
# 上传文件
def upload(file, title=u'选择要上载的文件,通过: 192.168.1.248'):
dialog = win32gui.FindWindow('#32770', title)
ComboBoxEx = win32gui.FindWindowEx(dialog, 0, 'ComboBoxEx32', None)
ComboBox = win32gui.FindWindowEx(ComboBoxEx, 0, 'ComboBox', None)
Edit = win32gui.FindWindowEx(ComboBox, 0, 'Edit', None)
button = win32gui.FindWindowEx(dialog, 0, 'Button', None)
time.sleep(0.5)
win32gui.SendMessage(Edit, win32con.WM_SETTEXT, 0, file)
time.sleep(0.5)
win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)
time.sleep(0.5)
上述代码实现了对windows上传文件窗口的操作(需要先试用用AutoIt或spy++定位控件的窗口名和句柄)
3.使用pywinauto
from pywinauto import application
import SendKeys
import time
class Pywin(object):
"""
pywin framwork main class
tool_name : 程序名称,支持带路径
windows_name : 窗口名字
"""
SLEEP_TIME = 1
def __init__(self):
"""
初始化方法,初始化一个app
"""
self.app = application.Application()
def run(self, tool_name):
"""
启动应用程序
"""
self.app.start_(tool_name)
time.sleep(1)
def connect(self, window_name):
"""
连接应用程序
app.connect_(path = r"c:\windows\system32\notepad.exe")
app.connect_(process = 2341)
app.connect_(handle = 0x010f0c)
"""
self.app.connect_(title = window_name)
time.sleep(1)
def close(self, window_name):
"""
关闭应用程序
"""
self.app[window_name].Close()
time.sleep(1)
def max_window(self, window_name):
"""
最大化窗口
"""
self.app[window_name].Maximize()
time.sleep(1)
def menu_click(self, window_name, menulist):
"""
菜单点击
"""
self.app[window_name].MenuSelect(menulist)
time.sleep(1)
def input(self, window_name, controller, content):
"""
输入内容
"""
self.app[window_name][controller].TypeKeys(content)
time.sleep(1)
def click(self, window_name, controller):
"""
鼠标左键点击
example:
下面两个功能相同,下面支持正则表达式
app[u'关于“记事本”'][u'确定'].Click()
app.window_(title_re = u'关于“记事本”').window_(title_re = u'确定').Click()
"""
self.app[window_name][controller].Click()
time.sleep(1)
def double_click(self, window_name, controller, x = 0,y = 0):
"""
鼠标左键点击(双击)
"""
self.app[window_name][controller].DoubleClick(button = "left", pressed = "", coords = (x, y))
time.sleep(1)
def right_click(self, window_name, controller, order):
"""
鼠标右键点击,下移进行菜单选择
window_name : 窗口名
controller:区域名
order : 数字,第几个命令
"""
self.app[window_name][controller].RightClick()
for down in range(order):
SendKeys.SendKeys('{DOWN}')
time.sleep(0.5)
SendKeys.SendKeys('{ENTER}')
time.sleep(1)
if __name__ == "__main__":
app = Pywin()
# 记事本例子
tool_name = "notepad.exe"
# 通过Spy++ 获取window_name,即标题文本
window_name = u"无标题 - 记事本"
menulist = u"帮助->关于记事本"
# 通过Spy++ 获取controller,即窗口类名
controller = "Edit"
content = u"johnny"
window_name_new = content + ".txt"
# 启动程序,记事本只能开一个
app.run(tool_name)
app.connect(window_name)
app.max_window(window_name)
app.menu_click(window_name,menulist)
app.click(u'关于记事本', u'确定')
app.input(window_name,controller,content)
# Ctrl + a 全选
app.input(window_name,controller,"^a")
# 选择复制
app.right_click(window_name,controller,3)
#选择粘贴
app.right_click(window_name,controller,4)
SendKeys.SendKeys('{ENTER}')
# Ctrl + v 粘贴
app.input(window_name,controller,"^v")
# Ctrl + s 保存
app.input(window_name,controller,"^s")
# 输入文件名
app.input(u"另存为",controller,content)
# 保存
app.click(u"另存为","Button")
try:
app.click(u"确认另存为","Button")
except:
pass
finally:
app.close(window_name_new)
七、页面元素失去焦点导致脚本运行不稳定
1:遇到脚本不稳定,有时会失去焦点导致测试失败的情况下,可以先切到焦点元素再进行操作。driver.switch_to.active_element
注意.active_element后面不带括号()。
下面是一个参考案例。
'最初的 “右击鼠标 → 新建文件夹 → 输入文件夹名称” 的代码'
l = driver.find_element_by_id('pm_treeRoom_1_span')
ActionChains(driver).context_click(l).perform()
driver.find_element_by_class_name('fnew').click()
time.sleep(2)
driver.find_element_by_xpath('//*[@id="pm_treeRoom_1_ul"]/li[...]').send_keys('filename')
time.sleep(2)
结果这种操作总会导致输入框失去焦点,直接消失,更不能send_keys进去了,直接报错。
'修改后的代码如下'
driver.find_element_by_class_name('fnew').click()
time.sleep(2)
driver.switch_to.active_element.send_keys('filename')
time.sleep(2)