元素定位
我们通过webdriver打开一个网络页面,目的是为了操作当前页面已完成浏览器中的一些UI测试步骤,所以必然需要操作网页。而网页的内容组成是由HTML标签(element,也叫元素),所以基于selenium操作网页实际上本质就是操作元素。那么要操作元素就必须先获取元素对象。selenium中关于元素的获取也叫元素的定位,提供了8种元素定位操作可以让测试人员获取一个或多个HTML元素。
定位类型 | 低版本代码 | 新版本代码 | 描述 |
---|---|---|---|
xpath |
driver.find_element_by_xpath(“xpath”)<br>driver.find_elements_by_xpath(“xpath”) | driver.find_element(By.XPATH, “xpath”)<br>driver.find_elements(By.XPATH, “xpath”) | 基于xpath路径表达式对HTML元素进行提取操作 |
selector | driver.find_element_by_css_selector(“css_selector”)<br>driver.find_elements_by_css_selector(“css_selector”) | driver.find_element(By.CSS_SELECTOR, “css_selector”)<br>driver.find_elements(By.CSS_SELECTOR, “css_selector”) | 基于css选择器对HTML元素进行提取操作 |
id | driver.find_element_by_id(“id”) | driver.find_element(By.ID,“id”) | 基于HTML元素的id属性对元素进行提取操作 |
class | driver.find_element_by_class_name(class_name")<br>driver.find_elements_by_class_name(class_name") | driver.find_element(By.CLASS_NAME, “class_name”)<br>driver.find_elements(By.CLASS_NAME, “class_name”) | 基于HTML元素的class属性对元素进行提取操作 |
tag | driver.find_element_by_tag_name(“tag_name”)<br>driver.find_elements_by_tag_name(“tag_name”) | driver.find_element(By.TAG_NAME, “tag_name”)<br>driver.find_elements(By.TAG_NAME, “tag_name”) | 基于HTML元素的标签名对元素进行提取操作 |
link_text | driver.find_element_by_link_text(“link_text”)<br>driver.find_elements_by_link_text(“link_text”) | driver.find_element(By.LINK_TEXT,“link_text”)<br>driver.find_elements(By.LINK_TEXT,“link_text”) | 基于HTML元素的超链接文本内容对元素进行提取操作 |
name | driver.find_element_by_name(“name”)<br>driver.find_elements_by_name(“name”) | driver.find_element(By.NAME, “name”)<br>driver.find_elements(By.NAME, “name”) | 基于HTML元素的name属性对元素进行提取操作,常用于表单元素 |
partial_link_text | driver.find_element_by_partial_link_text(“partial_link_text”)<br>driver.find_elements_by_partial_link_text(“partial_link_text”) | driver.find_element(By.PARTIAL_LINK_TEXT, “partial_link_text”)<br>driver.find_elements(By.PARTIAL_LINK_TEXT, “partial_link_text”) | 基于HTML元素的部分超链接对元素进行提取操作 |
基于xpath、css selector与class属性值获取元素
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
service = Service(executable_path="./chromedriver.exe")
driver = webdriver.Chrome(service=service)
# 打开一个页面
driver.get("https://www.luffycity.com")
# 基于xpath路径获取元素
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/img[1]')
# # 基于css选择符获取元素
# element = driver.find_element(By.CSS_SELECTOR, '#__layout > div > div.mbox > div > img.close')
# # 基于class属性值来获取元素
# element = driver.find_elements(By.CLASS_NAME, 'close')
# # 如果使用find_elements获取多个元素,返回结果是一个列表,需要通过下标来选择操作的是哪一个?
# element = element[0]
# 等待3秒以后点击(这个等待不是必须的,而是为了方便查看而已)
time.sleep(3)
print(element.click())
# 等待10秒退出,如果当前窗口只有一个浏览器页面,则表示退出当前浏览器
time.sleep(10)
driver.close()
基于id,link_text获取元素
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation']) # 规避网站监测
option.add_experimental_option('useAutomationExtension', False) # 去掉开发者警告
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'}) # 规避网站监测
# 打开一个页面
driver.get("https://www.baidu.com")
# 基于xpath路径获取元素
element = driver.find_element(By.ID, 'kw')
# 往输入框元素中通过键盘录入数据使用 send_keys()
element.send_keys("路飞学城")
# 找到提交按钮并点击
element = driver.find_element(By.ID, 'su')
element.click()
time.sleep(3)
# 从搜索结果找到有路飞学城的链接内容,并点击
element = driver.find_element(By.LINK_TEXT, '路飞学城')
element.click()
"""
错误提示:
NoSuchElementException: Message: no such element: Unable to locate element: {"method":"link text","selector":"路飞学城"}
异常,没有这样的一个元素:基于 {"method":"link text","selector":"路飞学城"} 查找不到元素(本地元素不可达)
原因是百度在用户搜索内容以后,采用了ajax进行异步刷新页面数据的,所以会导致selenium代码在元素时,百度服务器有可能没有返回数据结果,没有数据结果则没有该结果连接,因此报错
"""
# 等待10秒退出,如果当前窗口只有一个浏览器页面,则表示退出当前浏览器
time.sleep(10)
driver.close()
基于tag获取元素,往往用于获取iframe内嵌框架页中的元素。
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation']) # 规避网站监测
option.add_experimental_option('useAutomationExtension', False) # 去掉开发者警告
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'}) # 规避网站监测
# 打开一个页面
driver.get("https://mail.163.com")
iframe_list = driver.find_elements(By.TAG_NAME, 'iframe')
form_iframe = iframe_list[0]
# 切换窗口到指定iframe
driver.switch_to.frame(form_iframe)
driver.find_element(By.NAME, 'email').send_keys('moooluo2022')
time.sleep(3)
driver.find_element(By.NAME, 'email').clear()
# 等待10秒退出,如果当前窗口只有一个浏览器页面,则表示退出当前浏览器
time.sleep(10)
driver.close()
小练习:在上面的163邮箱登陆表单中,新增一段代码,实现自动输入密码操作。
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation']) # 规避网站监测
option.add_experimental_option('useAutomationExtension', False) # 去掉开发者警告
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'}) # 规避网站监测
# 打开一个页面
driver.get("https://mail.163.com")
iframe_list = driver.find_elements(By.TAG_NAME, 'iframe')
form_iframe = iframe_list[0]
# 切换窗口到指定iframe
driver.switch_to.frame(form_iframe)
driver.find_element(By.NAME, 'email').send_keys('moooluo2022')
# time.sleep(3)
# driver.find_element(By.NAME, 'email').clear()
driver.find_element(By.NAME, 'password').send_keys('123456')
driver.find_element(By.ID, 'dologin').click()
# 等待10秒退出,如果当前窗口只有一个浏览器页面,则表示退出当前浏览器
time.sleep(10)
driver.close()
元素操作
操作 | 描述 |
---|---|
click() | 点击当前元素 |
send_keys() | 给当前表单元素输入文本内容 |
clear() | 给当前表单元素清空内容 |
submit() | 点击提交按钮,提交表单,要使用submit提交表单则按钮必须有form表单才行,如果ajax提交的不行。 |
get_attribute() | 获取元素的指定属性值 |
value_of_css_property() | 获取元素的CSS样式的指定属性的属性值,不准确. |
is_displayed() | 查看元素是否显示可见 |
is_enabled() | 查看表单元素是否启用 |
is_selected() | 查看元素是否被选择,一般判断表单元素,如radio单选框或checkbox多选框 |
size | 获取元素的尺寸大小 |
text | 获取元素的文本内容 |
模拟登陆操作
代码:
import time
from getpass import getpass
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
def get_driver():
# 初始化一个谷歌浏览器实例对象
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service)
return driver
def task(username, password):
# 打开一个网址
driver.get("https://www.luffycity.com")
time.sleep(3) # 可以不用,这句代码的作用主要是便于观察
# 关闭广告
driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/img[1]').click()
# 点击登陆
driver.find_element(By.CLASS_NAME, 'signin').click()
time.sleep(3)
# 往账号输入框输入账号
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/div[2]/div[1]/div[1]/input')
element.send_keys(username)
# 往密码输入框输入密码
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/div[2]/div[1]/div[2]/input')
element.send_keys(password)
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/div[2]/button')
element.click()
time.sleep(5)
driver.quit()
if __name__ == '__main__':
username = input("账号: ")
# python的getpass会与pycharm内置的run终端形成阻塞,
# 所以使用了getpass,则需要改成cmd或pycharm的Terminal终端来运行python脚本
password = getpass("密码:")
driver = get_driver()
task(username, password)
查看操作元素的相关属性
import time
from getpass import getpass
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
# 初始化一个谷歌浏览器实例对象
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service)
# 打开一个网址
driver.get("http://localhost:63342/code/7.html?_ijt=gom325r5bev2q5u5q3uelkchje")
# # 查看元素的显示模式,是否是可见
# element = driver.find_element(By.NAME, 'access_token')
# display = element.is_displayed()
# print(display)
#
# # 查看元素的属性值
# print(element.get_attribute("value"))
# outline = element.value_of_css_property('border')
# print(outline)
# element = driver.find_element(By.XPATH, '/html/body/form/input[4]')
# enabled = element.is_enabled()
# print(enabled)
# # 提交表单
# element = driver.find_element(By.XPATH, '/html/body/form/input[5]')
# element.submit()
element = driver.find_element(By.XPATH, '/html/body/h1')
print(element.text)
time.sleep(5)
driver.quit()
规避监测
现在不少网站有对selenium采取了监测机制。这些监测机制会导致我们使用selenium访问网站时出现各种拦截机制,如出现验证码等。
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service)
driver.get("https://www.baidu.com")
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
time.sleep(3)
driver.quit()
解决方案:
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation']) # 规避网站监测
option.add_experimental_option('useAutomationExtension', False) # 去掉开发者警告
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'}) # 规避网站监测
driver.get("https://www.baidu.com")
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
time.sleep(5)
driver.quit()
鼠标事件
在上面的selenium操作中,我们获取元素可以直接调用click方法实现鼠标点击效果。但是,如果要进行更加复杂多样的鼠标操作,如鼠标右键、鼠标移动、拖动等等,则需要依赖于 ActionChains (动作链) 模块提供的鼠标操作来完成。
from selenium.webdriver.common.action_chains import ActionChains
基本使用
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
# 初始化一个谷歌浏览器实例对象
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
# 打开一个网址
driver.get("https://www.luffycity.com")
# 关闭广告
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/img[1]')
element.click()
time.sleep(5)
# 定位元素,定位到顶部导航菜单的"题库"上面
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/header/div/div/nav/a[6]')
# move_to_element 把鼠标悬停在指定元素上面
# context_click 执行鼠标右键
# 动作链在设置动作以后,不会自动执行,所以需要在动作链的末尾,调用perform执行整个动作链
ActionChains(driver).move_to_element(element).context_click().perform()
time.sleep(5)
driver.quit()
ActionChains动作链可以保存1个或多个连贯的鼠标操作,但是最终执行鼠标操作需要调用perform()
方法。常用鼠标操作:
操作 | 描述 |
---|---|
double_click(element) | 鼠标双击 |
context_click(element) | 鼠标右击 |
drag_and_drop(source, target) | 鼠标拖动(将一个元素移动到另一个元素位置) |
drag_and_drop_by_offset(source, xoffset, yoffset) | 鼠标拖动(将一个元素移动到指定坐标位置) |
move_to_element(element) | 鼠标移动并悬放到指定元素的上方 |
move_by_offset(xoffset, yoffset) | 将鼠标从当前位置移动到指定坐标处 |
测试页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.box{
width: 100px;
height: 100px;
background: red;
}
.box:hover{
background: blue;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
var box = document.querySelector(".box")
box.ondblclick = function(){
box.style.background = "yellow";
}
</script>
</body>
</html>
测试双击与鼠标选放
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service)
# 打开一个网址
driver.get("C:/Users/Administrator/Desktop/code/11-测试页面.html")
element = driver.find_element(By.CLASS_NAME, 'box')
ActionChains(driver).move_to_element(element).perform()
time.sleep(1)
ActionChains(driver).double_click().perform()
time.sleep(3)
driver.quit()
拖拽元素
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions, ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.keys import Keys
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
# 设置浏览器窗口最大化
driver.maximize_window()
# 进入页面
driver.get('https://www.jq22.com/yanshi10850')
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)
element1 = driver.find_element(By.XPATH, '//*[@id="bar"]/li[1]')
element2 = driver.find_element(By.XPATH, '//*[@id="foo"]')
time.sleep(3)
ActionChains(driver).drag_and_drop(element1, element2).perform()
time.sleep(10)
driver.quit()
键盘事件
前面我们在元素操作中使用了send_keys()方法来模拟键盘输入,而针对复杂的组合键盘操作,webdriver提供了键盘上几乎所有的按键方法,使用前导入Keys类即可。
from selenium.webdriver.common.keys import Keys
方法名 | 描述 |
---|---|
send_keys(Keys.ENTER) | 回车键Enter |
send_keys(Keys.BACK_SPACE) | 删除键BackSpace |
send_keys(Keys.SPACE) | 空格键(Space) |
send_keys(Keys.TAB) | 制表键(Tab) |
send_keys(Keys.ESCAPE) | 复位键Esc |
send_keys(Keys.CONTROL,‘a’) | 全选Ctrl+A |
send_keys(Keys.CONTROL,‘c’) | 复制Ctrl+C |
send_keys(Keys.CONTROL,‘x’) | 剪切Ctrl+X |
send_keys(Keys.CONTROL,‘v’) | 粘贴Ctrl+V |
send_keys(Keys.F1) | 键盘F1 |
…… | …… |
send_keys(Keys.F12) | 键盘F12 |
基本使用
页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
</style>
</head>
<body>
<textarea name="content" id="" cols="30" rows="10">hello!EveryBody!</textarea>
</body>
</html>
代码:
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.keys import Keys
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service)
driver.get("C:/Users/Administrator/Desktop/code/12-测试页面.html")
time.sleep(3)
element = driver.find_element(By.TAG_NAME, "textarea")
element.send_keys(Keys.CONTROL, 'a')
element.send_keys(Keys.BACK_SPACE)
time.sleep(3)
element.send_keys("2012年9月25日,辽宁舰正式交付中国海军;2019年12月17日,山东舰入列,中国海军进入双航母时代;2022年6月17日,福建舰来了,我们有三艘航母了。")
time.sleep(5)
driver.quit()
回车键等操作
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.keys import Keys
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
# 设置浏览器窗口最大化
driver.maximize_window()
# driver.minimize_window() # 最小化
# 进入页面
driver.get('https://www.baidu.com/')
# 定位输入框
element = driver.find_element(By.ID, "kw")
# 向输入框中输入内容
element.send_keys("路飞学城")
time.sleep(2)
# 删除上一步中多输入的文字
element.send_keys(*[Keys.BACK_SPACE for i in range(2)])
element.send_keys("在线教育")
time.sleep(2)
# 使用回车代替点击按钮
element.send_keys(Keys.ENTER)
time.sleep(2)
屏幕截图
在selenium中,截取网页的图片有多种方式,一般常用的有2种方式:截取可见区域和截取指定区域。
截取可见区域
主要是通过driver.save_screenshot
来完成。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ChromeOptions
from selenium.webdriver.chrome.service import Service
# 初始化一个谷歌浏览器实例对象
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{'source': 'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
driver.get('https://www.luffycity.com')
# 关闭广告
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/img[1]')
element.click()
# 调用浏览器驱动对象的save_screenshot方法就可以实现截图
driver.save_screenshot('./luffycity.png') # 图片必须保存为 png 格式
driver.quit()
save_screenshot使用过程中,需要注意2点:
- save_screenshot方法保存的图片格式必须是png格式,否则不行。
- save_screenshot保存图片的目录,必须是已经存在的,否则无法保存图片。
截取指定区域
通过先获取HTML文档中的元素对象,再通过元素对象调用screenshot
来进行截取。
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ChromeOptions
from selenium.webdriver.chrome.service import Service
# 初始化一个谷歌浏览器实例对象
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{'source': 'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
driver.get('https://www.luffycity.com')
# 关闭广告
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/img[1]')
element.click()
# 点击登陆
driver.find_element(By.CLASS_NAME, 'signin').click()
time.sleep(3)
# 往账号输入框输入账号
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/div[2]/div[1]/div[1]/input')
element.send_keys("13928835901")
# 往密码输入框输入密码
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/div[2]/div[1]/div[2]/input')
element.send_keys("123")
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/div[2]/button')
element.click()
time.sleep(2)
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div')
# 通过元素的 screenshot 方法直接保存图片
element.screenshot('./login_result.png')
time.sleep(3)
driver.quit()
等待机制
针对一些通过ajax局部刷新或延时显示的数据,在selenium使用过程中如果没有等待数据显示就直接执行后续代码就会有问题。
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("C:/Users/Administrator/Desktop/test/测试自动化-day04/素材/chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
driver.get("http://www.baidu.com")
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
# time.sleep(3)
driver.find_element(By.PARTIAL_LINK_TEXT, "路飞学城").click()
time.sleep(3)
driver.quit()
上面虽然使用了time.sleep(3)
让selenium强制等待了3秒,但是这种操作不够灵活,因为数据加载有时候不需要3秒就显示出来了,而程序并没有办法知道而导致最终呆呆的等待了3秒才继续执行。当然,也有可能3秒后数据还没有出来,那么程序则依然报错。所以开发中针对上述问题,我们是不能使用time.sleep
来草草了事的,而是要使用selenium提供的另外两种等待机制。
显式等待
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
driver.get("http://www.baidu.com")
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
# 使用time.sleep这种机械化的强制等待,实际上是有可能出问题的。即便没有问题,也会存在着让测试代码浪费等待时间的情况。
# time.sleep(3)
# driver.find_element(By.PARTIAL_LINK_TEXT, "路飞学城").click()
# 显式等待
WebDriverWait(driver=driver, timeout=60, poll_frequency=0.5, ignored_exceptions=None).until(expected_conditions.presence_of_element_located((By.PARTIAL_LINK_TEXT, '路飞学城'))).click()
time.sleep(3)
driver.quit()
selenium
提供了 WebDriverWait
类实现显式等待机制,WebDriverWait
类接收4个参数来指定等待机制。
- driver,浏览器驱动对象
- timeout,最长超时等待时间,单位(秒)
- poll_frequency,轮询检测等待条件的时间,也就是每隔多长时间检测一次条件,默认是0.5秒
- ignored_exceptions,timeout超时后的异常信息,默认抛出
NoSuchElementException
。
基本使用:
WebDriverWait(driver=driver, timeout=10, poll_frequency=0.5, ignored_exceptions=None)
WebDriverWait
类提供了两个方法来完成等待条件的检测实现:
- until(self, method, message=‘’),method为等待检测条件的判断方法,until表示直到返回True,用的较多。
- until_not(self, method, message=‘’),method为等待检测条件的判断方法,until_not表示直到返回False。
上面代码用到的method指定为expected_conditions
模块并调用其presence_of_element_located
方法判断指定元素是否存在。
expected_conditions
模块提供了各种判断:
presence_of_element_located
判断某个元素是否存在于HTML文档中。presence_of_elements_located
判断是否至少有1个元素存在HTML中。只要有1个元素存在,则该方法就返回True。
显式等待的另一种写法,首先实例化一个显式等待对象,这样可以在各个地方灵活的调用:
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
# 1. 先创建一个显式等待对象
wait = WebDriverWait(driver=driver, timeout=60, poll_frequency=0.5, ignored_exceptions=None)
driver.get("http://www.baidu.com")
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
# 使用time.sleep这种机械化的强制等待,实际上是有可能出问题的。即便没有问题,也会存在着让测试代码浪费等待时间的情况。
# time.sleep(3)
# driver.find_element(By.PARTIAL_LINK_TEXT, "路飞学城").click()
# 2. 显式等待对象调用判断等待方法
wait.until(expected_conditions.presence_of_element_located((By.PARTIAL_LINK_TEXT, '路飞学城'))).click()
time.sleep(3)
driver.quit()
隐式等待
也叫全局等待。我们可以直接通过浏览器驱动对象driver调用隐式等待,并且用法也相对简单:
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver=webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
# 设置隐式等待
driver.implicitly_wait(time_to_wait=10) # 只需要一个等待超时时间参数
driver.get("https://www.baidu.com")
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
driver.find_element(By.PARTIAL_LINK_TEXT, "路飞学城").click()
time.sleep(3)
driver.quit()
implicitly_wait
等待时间单位为秒,如上例所示,我们指定了10秒。需要注意的是,隐式与显式等待有明显的区别,隐式等待应用于全局,每当使用driver
驱动找某个元素时,隐式等待机制就会被触发(导致测试代码的运行速度变慢),如果元素存在,则继续执行,否则,它将以轮询的方式判断元素是否定位成功,直至等待超时,抛出错误NoSuchElementException
。而显式等待则只是指定某(些)个元素是否存在时执行。
在等待机制的选择上,我们可以在三种等待机制中灵活选择:
- 普通(静态页面较多)网页,强制等待和显式等待可以相互搭配,提高效率。适用于前后端不分离的UI场景测试。
- 动态页面较多的时候,则可以全局设置隐式等待。适用于前后端分离的UI场景测试
窗口切换
在测试客户端时,往往存在有些功能流程需要点击打开并操作多个窗口页面的情况。此时就需要使用selenium在多个窗口中相互切换操作了。selenium中提供了三个方法给测试开发人员完成窗口切换操作。
方法名 | 描述 |
---|---|
driver.switch_to.window | 切换普通窗口 |
driver.switch_to.frame | 切换进入iframe窗口 |
driver.switch_to.default_content | 切换退出iframe窗口 |
driver.window_handles | 窗口数组 |
切换普通窗口
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait # 等待页面加载某些元素
from selenium.webdriver.support import expected_conditions
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{'source': 'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
wait = WebDriverWait(driver, 10)
try:
driver.get("https://www.baidu.com")
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
wait.until(expected_conditions.presence_of_element_located((By.LINK_TEXT, '路飞学城'))).click()
# 查看当前浏览器中打开的窗口列表
print(driver.window_handles)
time.sleep(3)
# 根据数组下标索引切换窗口
driver.switch_to.window(driver.window_handles[0])
time.sleep(3)
driver.find_element(By.LINK_TEXT, '百度百科').click()
print(driver.window_handles) # 此时应该有3个窗口了
time.sleep(3)
driver.switch_to.window(driver.window_handles[1]) # 切换到路飞窗口
finally:
time.sleep(3)
driver.quit()
切换iframe窗口
两个方法:
- switch_to.frame(iframe),进入窗口
- switch_to.default_content(),退出窗口
import time
import getpass
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
def get_driver():
"""获取浏览器驱动对象"""
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{'source': 'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'})
# 设置隐式等待
driver.implicitly_wait(10)
return driver
def task(url, username, password, to, theme):
"""
发送邮件
:param url 打开站点地址
:param username 登陆账号
:param password 登陆密码
:param to 收件人邮箱账号
:param theme 邮件的主体
"""
driver.get(url)
iframe = driver.find_elements(By.TAG_NAME, 'iframe')
driver.switch_to.frame(iframe[0])
driver.find_element(By.CLASS_NAME, 'dlemail').send_keys(username)
driver.find_element(By.CLASS_NAME, 'dlpwd').send_keys(password)
driver.find_element(By.ID, 'dologin').click()
# 登陆以后刷新了页面了,所以需要重新获取driver浏览器驱动对象,否则无法进行后续操作
driver.switch_to.window(driver.window_handles[0])
driver.find_element(By.ID, '_mail_component_149_149').click()
driver.find_element(By.CLASS_NAME, 'nui-editableAddr-ipt').send_keys(to)
driver.find_elements(By.CLASS_NAME, 'nui-ipt-input')[2].send_keys(theme)
content_iframe = driver.find_element(By.CLASS_NAME, 'APP-editor-iframe')
driver.switch_to.frame(content_iframe)
driver.find_element(By.TAG_NAME, "p").send_keys("这是通过selenium添加的邮件内容!!!")
driver.switch_to.default_content()
# 发送邮件
driver.find_elements(By.CLASS_NAME, 'nui-btn-text')[2].click()
if __name__ == '__main__':
url = 'https://mail.163.com'
theme = '一份测试邮件'
to = '[email protected]'
content = '测试邮件内容.................................'
# username = input('用户名: ').strip()
username = "moooluo2022"
# pycharm中直接运行python脚本会导致getpass进入阻塞,所以需要使用终端命令行运行python脚本
password = getpass.getpass(prompt="密码: ")
driver = get_driver()
try:
task(url, username, password, to, content)
except Exception as e:
print(f"发送邮件出错:{e}")
finally:
time.sleep(20)
driver.quit()
执行js代码
实际上,我们之前有提到关于selenium实际上操作浏览器都是通过webdriver完成对应的功能操作的,而webdriver实际上是通过js代码来控制浏览器的。因此针对如果selenium本身如果没有提供的操作,则我们可以使用js代码来完成一些窗口操作。例如:滚动条。
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation']) # 规避网站监测
option.add_experimental_option('useAutomationExtension', False) # 去掉开发者警告
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'}) # 规避网站监测
driver.get("https://www.baidu.com")
# # 执行没有返回的值的js代码
# driver.execute_script('confirm("hello, luffycity!")')
#
# # 关闭alert弹窗
# time.sleep(3)
# # 让driver浏览器驱动对象,切换焦点(切换上下文)到弹窗
# alert = driver.switch_to.alert
# # 点击确定,以达到关闭弹窗的效果
# # alert.accept()
# alert.dismiss() # 关闭弹窗,相当于点击了取消
"""让浏览器执行js代码并获取代码的返回值"""
"""例如:获取cookie"""
# result = driver.execute_script('return document.cookie')
# cookies = result.split(";")
# cookies_dict = {i[0]:i[1] for i in [item.split("=") for item in cookies]}
# print(cookies_dict.get("BIDUPSID"))
# 有了cookie以后,以后就可以跳过登陆测试其他的功能操作了。
"""例如:获取本地存储中的token"""
result = driver.execute_script("return localStorage")
print(result) #
time.sleep(5)
driver.quit()
操作滚动条
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service)
driver.get("https://www.luffycity.com")
# 关闭广告
element = driver.find_element(By.XPATH, '//*[@id="__layout"]/div/div[2]/div/img[1]')
element.click()
num = 0
while num <= driver.execute_script("""return parseInt(getComputedStyle(document.querySelector('.main'))["height"])"""):
num += 1
driver.execute_script(f"window.scrollTo(0, {20*num})")
time.sleep(0.01)
time.sleep(3)
driver.quit()
无头浏览器
所谓的无头浏览器(Headless),实际上就是不需要打开浏览器去操作浏览器完成对应UI效果。
import time
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
option = ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation']) # 规避网站监测
option.add_experimental_option('useAutomationExtension', False) # 去掉开发者警告
# 开启浏览器的无头模式
option.add_argument('--headless')
option.add_argument('--disable-gpu') # 允许在无GPU的环境下运行,可选
option.add_argument('--window-size=1920x1080') # 建议设置
service = Service("chromedriver.exe")
driver = webdriver.Chrome(service=service, options=option)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefind})'}) # 规避网站监测
# 设置隐式等待
driver.implicitly_wait(time_to_wait=10) # 只需要一个等待超时时间参数
# 打开网页
driver.get("https://www.baidu.com")
# 搜索内容
driver.find_element(By.ID, "kw").send_keys("路飞学城")
driver.find_element(By.ID, "su").click()
# 点击搜索结果
driver.find_element(By.PARTIAL_LINK_TEXT, "路飞学城").click()
# 调用浏览器驱动对象的save_screenshot方法就可以实现截图
driver.save_screenshot('./baidu_search_result.png') # 图片必须保存为 png 格式
driver.quit()
使用无头浏览器,在运行一些经过验证的测试脚本时,可以达到节约系统资源的优点,而且少了浏览器的UI效果,UI测试脚本的运行速度会加快。
APP自动化测试
APP移动端测试是指对移动端应用进行的测试,测试应用功能是否满足特定的需求。移动端测试一般分3类:APP功能测试、APP自动化测试、APP安全测试。其中常用的APP自动化测试工具有:Robtium、macaca、Appium、UiAutomator、Monkey、MonkeyRunner、Instrumentation、Athrun等等。
工具 | 支持语言 | 跨平台 | 跨应用 | |
---|---|---|---|---|
Robotium | Java | Android | 不支持 | |
Macaca | Java、Python、node.js | Android、IOS | 支持 | 可以作为简洁版的Appium工具 |
Appium | Java、c#、Python、PHP、Perl、Ruby、node.js | Android、IOS、H5 | 支持 |
Appium
基本介绍
Appium由Sauce labs公司(是美国的一家提供自动化的软件测试服务公司)基于node.js
的express
框架开发http server
,是一个开源、跨平台的移动端自动化测试框架,可以用来测试原生及混合的移动端应用。Appium支持IOS、Android及H5等移动端应用。Appium使用WebDriver的json wire协议来驱动Apple系统的UIAutomation库(由苹果官方提供的自动化测试库)、Android系统的UIAutomator框架(由Android官方提供的自动化测试库)。
与selenium的关系
appium继承了selenium的webdriver,也就是selenium2.0,所以appium在调试中实际上也调用了selenium的某些功能。
appium起到了一个电脑连接移动端的桥梁,然后我们可以在电脑上调用selenium工具来调试移动端应用。
appium原理
appium本质上就是一个使用node.js编写的http server(Http服务器),它对外暴露了一系列REST API接口提供给测试开发人员进行调用。
这个http server的功能其实很简单:监听一个端口,然后接收由client发送来的command。翻译这些command,把这些command转成移动设备可以理解的形式发送给移动设备,然后移动设备执行完这些command后把执行结果返回给httpserver,http server再把执行结果返回给client。
在这里client其实就是发起command的设备,一般来说就是我们编写代码执行的机器,执行appium测试代码的机器。狭义点理解,可以把client理解成是代码,当然这些代码可以是java/ruby/python/js的,只要它实现了webdriver标准协议就可以。
这样的设计思想带来了一些好处:
- 可以带来多语言的支持;
- 可以把server放在任意机器上,哪怕是云服务器都可以;(是的,appium和webdriver天生适合云测试)
安装步骤
安装java环境
目前,java环境普遍来说使用的都是java8,所以我们直接下载安装1.8版本的sdk即可(jdk是Java语言的软件开发工具包,是整个java开发的核心,它包含了JAVA的运行环境,JAVA工具和JAVA基础的类库)。
下载地址:https://www.oracle.com/java/technologies/downloads/
鼠标右键安装(注意,不要把它安装到有中文的路径下,所以最好默认路径即可),一路点击确定下一步即可完成安装。
注意:javasdk环境是依赖于JAVA_HOME
的,依次打开控制面板
▷系统与安全
▷系统
▷高级系统设置
▷环境变量
▷系统变量
▷新建
。变量名中输入JAVA_HOME
,变量值中填写刚才获取到的路径C:\tool\Java\jdk1.8.0_201
(注意,此处根据自己的实际路径填写,别瞎复制)。
保存了JAVA_HOME环境变量,点击下方的Path环境变量,里面把java sdk安装目录下bin目录加入到环境变量中。
cmd终端输入java -version
,出现如下内容表示安装完成。
安装Android SDK
[Android SDK](https://baike.baidu.com/item/Android SDK)(software development kit,译作:软件开发工具包),是用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。Android SDK 指的是Android专属的软件开发工具包。
下载地址:https://www.androiddevtools.cn/#sdk-tools
把下载下来的sdk压缩包解压到一个没有中文路径的目录下。例如:我的是 C:/tool/
解压完成以后,进入sdk目录下,点击 SDK Manager.exe(sdk开发工具管理器,提供了类似pip的功能)。
按下面图中所示,勾选安卓开发最基本的7个模块到本地。
安装完成以后,Android SDK Tools会在安装成功以后,从上面隐藏掉,不用在意。
接下来,需要把以下三个路径加入到环境变量中,让系统能识别 android sdk(注意根据自己实际的解压路径填写,不要瞎复制)。
C:\tool\android-sdk-windows\tools
C:\tool\android-sdk-windows\platform-tools
C:\tool\android-sdk-windows\build-tools\29.0.3
cmd终端下输入命令adb
,出现如下效果则安装正确。
- adb.exe( Android Debug Bridge,安卓调试桥),调试app用的命令行工具,借助这个工具,我们可以直接在PC端就可以通过USB或者wifi就可以管理设备或模拟器的状态。保存在
android-sdk-windows\platform-tools
。 - aapt.exe(Android Asset Packaging Tool),安卓开发使用的一个自动资源打包工具,可以用来检测apk的包名和activity名称。保存在
android-sdk-windows\build-tools\<安卓sdk版本>
安装模拟器
appium既支持使用真机测试,也支持模拟器测试。不过我们测试过程中实际上也并非一定要使用真机,因为我们测试的并非是移动端的操作系统而是app本身,所以很多时候测试开发过程中使用模拟器会更加方便,最关键的是模拟器可以轻松切换不同版本。这里推荐使用夜神模拟器(它不仅有windows版本,也有ios版本,而像其他的雷神模拟器,则仅支持windows版本,没有ios版本)。
提示:如果windows下同时安装了安卓模拟器和docker-desktop的同学,如果无法顺利启动模拟器,可能需要执行以下命令,切换模拟器与docker-desktop的虚拟化服务。
# 使用模拟器
bcdedit /set hypervisorlaunchtype off
# 使用docker-desktop
bcdedit /set hypervisorlaunchtype auto
模拟器默认在安装以后会在bin目录下提供一个adb.exe安卓调试桥接工具(夜神模拟器叫nox_adb.exe),但是这个工具的版本有可能与我们安装的android sdk的abd.exe版本不一样,这样会导致无法使用appium调试模拟器中的移动端应用,所以需要把android sdk的abd.exe复制并改名替换掉模拟器bin目录下的adb.exe。真机测试则没有这个问题。
找到android sdk安装目录下platform-tools目录下的adb.exe。
找到夜神安装目录的bin目录下的nox_adb.exe,并备份,然后替换成上面的。
同时打开模拟器中的配置,修改为手机版面,并开启开发者模式,并勾选开启USB调试功能。
点击7下召唤神龙~。。。。说错了,重来,点击7点是打开当前设备的开发者选项。
返回上一步,就可以看到多了一个开发者选项了,接下来,开启USB调试选项。
安装appium
因为版本更新的原因,实际上appium是存在着2个分支版本的,分别是appium Server 与 appium Desktop。其中,appium Server的安装比较复杂需要先安装node.js,所以目前来说官方已经不再更新与维护appium Server分支了,而是推荐测试开发者安装 appium Desktop版本。所以此处,我们直接通过以下链接到github下根据自己当前使用的操作系统安装appium Desktop即可。
下载地址:https://github.com/appium/appium-desktop/releases
下载到本地以后双击exe文件,一路默认安装即可。
完成上面操作以后,打开终端输入adb devices
,看到如下效果,则表示安装环境全部完成。
> adb devices的结果是当前连接到PC端的所有的移动端设备列表。左侧参数是设备ID,右侧参数是设备的连接状态。 > > 如果使用模拟器则显示效果如下: > 127.0.0.1:5555 device # 表示雷电模拟器 > 127.0.0.1:62001 device # 表示夜神模拟器 > > 如果使用真机则显示效果如下: > UJN0221722001562 device
注意:鸿蒙手机实际上虽然不是安卓系统,但是鸿蒙手机为了兼容安卓,实际上也提供了adb调试,但是很遗憾的是,我们不能直接使用USB调试,只能改用wifi调试才可以测试鸿蒙系统下的app。
adb命令
以下是开发中比较常用的adb命令:
命令 | 描述 |
---|---|
adb devices |
查询连接到系统的所有移动端设备状态,常见状态:<br>device:连接正常<br/>offline:已断开<br/>unauthorized:未授权(手机端弹出的调试框没有允许) |
adb -s 设备ID | 指定设备使用adb,设置ID通过上面adb devices可以获取 |
adb -s 设备ID shell [命令] | 使用Android Linux内核的命令 |
adb -s 设备ID install -r 包名+路径 | 安装apk包,-r表示覆盖安装,可以使用aapt d badging apk文件路径 查看app包名。 |
adb -s 设备ID uninstall 包名 | 卸载apk |
adb -s 设备ID push 电脑路径 手机路径 | 电脑文件上传至手机 |
adb -s 设备ID pull 手机路径 电脑路径 | 手机文件下载至电脑 |
adb -s 设备ID shell pm list packages |
列出手机中装的所有APP的包名 |
adb -s 设备ID shell pm clear apk包名 |
清除应用数据及缓存 |
**`adb -s 设备ID shell dumpsys window windows | grep mFocusedApp`** |
安装:
# 通过自动打包工具aapt,查看下载回来的apk的包名,并把源apk文件名改成包名。
aapt d badging C:\Users\Administrator\Desktop\test\jd.apk
# 通过adb指定设备,进行安装
adb -s 127.0.0.1:62001 install C:\Users\Administrator\Desktop\test\com.jingdong.app.mall.apk
# 卸载apk包
adb -s 127.0.0.1:62001 uninstall com.jingdong.app.mall
安装appium-inspector
Appium Inspector是appium Server UI内置的一个元素定位工具,在早期版本中是内置的,新版本已经分离了,所以可以通过以下链接进行安装。
下载地址:https://github.com/appium/appium-inspector/releases
安装Appium-Python-Client
Python想要操作appium,就要有专门的的连接工具模块-appium-python-client。直接安装即可。
pip install appium-python-client
OK。经过上面的步骤以后,appium的安装步骤就全部完成了。
基本使用
- 打开appium,点击startServer。
-
打开模拟器或真机,选择要启动的应用,通过cmd终端查找当前应用的包名(appPackage)与激活入口地址(appActivity)。
例如我们在模拟器中选择并打开“设置“。
adb devices adb -s 127.0.0.1:62001 shell dumpsys window windows | grep mFocusedApp # adb shell dumpsys window windows | grep mFocusedApp
效果:
-
通过python操作移动端打开“设置”应用。
import time from appium.webdriver.webdriver import WebDriver # Appium 服务器初始化参数 desired_capabilities = { "platformName": "Android", # 系统类型 IOS/Android "platformVersion": "7", # 操作系统版本 "deviceName": "127.0.0.1:62001", # 设备类型,设备ID,IOS必须填写 # adb shell dumpsys window windows | grep mFocusedApp "appPackage": "com.jingdong.app.mall", # 要打开的APP包名 "appActivity": ".MainFrameActivity" # APP激活的首屏名称 } driver = WebDriver("http://127.0.0.1:4723/wd/hub", desired_capabilities) time.sleep(5) driver.quit()
jsonwp协议
在前面的Appium基本介绍时,我们提到过Appium Server实际上就是一个HTTP服务器,它对外暴露了一系列的restful API接口给开发者进行远程调用。
所以我们上面编写的python代码打开设置应用这个过程,本质上就是通过jsonwp(json wire protocol, WebDriver 开发者编写的一种通信协议)协议来实现对远程APP的操作的。
请求方法 | uri地址 | 描述 |
---|---|---|
POST | /session | 新建会话 |
DELETE | /session/会话ID | 删除会话 |
GET | /status | 获取appium服务器状态信息 |
更多restful API接口操作:https://w3c.github.io/webdriver/#x6-5-endpoints
postman操作
// 新建会话
// POST http://127.0.0.1:4723/wd/hub/session
{
"desiredCapabilities": {
"platformName": "Android",
"platformVersion": "7",
"deviceName": "127.0.0.1:62001",
"appPackage": "com.android.settings",
"appActivity": ".Settings"
}
}
// 删除会话【会话ID在上面的请求操作的响应字典中可以找到】
// DELETE http://127.0.0.1:4723/wd/hub/session/d9cd07ad-1170-49fd-99cc-a4fb4f4e4435
capability
初始化参数(Capability)是JSON数据类型编码的键和值,当一个新的自动化会话被请求时,Appium客户端发送此参数到服务端。此参数传递到Appium drivers用于设置各种重要事项测试工作。每种客户端语言都有特定的Appium客户端构建参数,但最终他们以JSON数据发送到Appium Server的。
初始化参数(Capability)可以编写在WebDriver测试代码中或者存放在Appium Server GUI中(Inspector会话)。
启动参数文档:https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md
中文翻译文档:https://github.com/appium/appium/blob/master/docs/cn/writing-running-appium/caps.md
通用参数
键 | 描述 | 值 |
---|---|---|
automationName |
自动化测试的引擎 | Appium (默认)或者 Selendroid |
platformName |
使用的手机操作系统 | iOS , Android , 或者 FirefoxOS |
platformVersion |
手机操作系统的版本 | 例如 7.1 , 4.4 |
deviceName |
使用的手机或模拟器设备名称 | iPhone Simulator , iPad Simulator , iPhone Retina 4-inch , Android Emulator , Galaxy S4 , 等等… 在 iOS 上,使用 Instruments 的 instruments -s devices 命令可返回一个有效的设备的列表,例如:iPhone X等。在 Andorid 上虽然这个参数目前已被忽略,但仍然需要添加上该参数。 |
app |
本地绝对路径_或_远程 http URL 所指向的一个安装包(.ipa ,.apk ,或 .zip 文件)。Appium 将其安装到合适的设备上。请注意,如果您指定了 appPackage 和 appActivity 参数(见下文),Android 则不需要此参数了。该参数也与 browserName 不兼容。 |
/abs/path/to/my.apk 或 http://myapp.com/app.ipa |
browserName |
做自动化时使用的浏览器名字。如果是一个应用则只需填写个空的字符串 | ‘Safari’ 对应 iOS,‘Chrome’, ‘Chromium’, 或 ‘Browser’ 则对应 Android |
newCommandTimeout | 用于客户端在退出或者结束 session 之前,Appium 等待客户端发送一条新命令所花费的时间(秒为单位) | 例如 60 |
language | (Sim/Emu-only) 为模拟器设置语言 | 例如 fr |
locale | (Sim/Emu-only) 为模拟器设置所在区域 | 例如 fr_CA |
udid |
连接真机的唯一设备号 | 例如 1ae203187fc012g |
orientation | (Sim/Emu-only) 模拟器当前的方向 | 竖屏 或 横屏 |
autoWebview | 直接转换到 Webview 上下文(context)。默认值为 false |
true , false |
noReset |
在当前 session 下不会重置应用的状态。默认值为 false |
true , false |
fullReset | (iOS)删除所有的模拟器文件夹。(Android) 要清除 app 里的数据,请将应用卸载才能达到重置应用的效果。在 Android, 在 session 完成之后也会将应用卸载掉。默认值为 false |
true , false |
Android 独有
键 | 描述 | 值 |
---|---|---|
appActivity |
Activity 的名字是指从你的包中所要启动的 Android acticity。他通常需要再前面添加. (例如 使用 .MainActivity 代替 MainActivity ) |
MainActivity , .Settings |
appPackage |
运行的 Android 应用的包名 | com.example.android.myApp , com.android.settings |
appWaitActivity | 用于等待启动的 Android Activity 名称 | SplashActivity |
appWaitPackage | 用于等待启动的 Android 应用的包 | com.example.android.myApp , com.android.settings |
appWaitDuration | 用于等待 appWaitActivity 启动的超时时间(以毫秒为单位)(默认值为 20000 ) |
30000 |
device |