测试开发笔记五(web自动化测试)
2020-05-07 本文已影响0人
提摩太_e9ec
01 | selenium安装
简介
- 支持web浏览器的自动化
- 由3个工具构成:webdriver、ide、grid
- 官网:http://www.selenium.dev/
selenium架构图
image.png
selenium环境配置步骤
- 准备好python环境
- 准备好selenium环境
1.pip install selenium - 下载浏览器对应的driver版本
1.淘宝镜像:https://npm.taobao.org/mirrors/chromedriver/ - driver配置环境变量
1.验证chromedriver - python中import对应依赖
02 | selenium测试用例编写
官方文档
https://selenium-python.readthedocs.io/
示例
from selenium import webdriver
class TestHogwarts():
def setup(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_selenium(self):
self.driver.get("http://www.testerhome.com")
self.driver.find_element_by_link_text("社团").click()
self.driver.find_element_by_link_text("霍格沃兹测试学院").click()
03 | 隐式等待与显式等待
直接等待
- 强制等待,线程休眠一定时间
- 能不用尽量不用
time.sleep(3)
隐式等待
- 设置一个等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常
- 是个全局方法,作用于所有find(查找元素的方法)
self.driver.implicitly_wait(3)
显示等待
- 在代码中定义等待条件,当条件发生时才继续执行代码
- WebDriverWait配合until()和until_not()方法,根据判断条件进行等待
- 每个一段时间(默认0.5s)进行条件判断,如果条件成立,则执行下一步,否则继续等待,直到超过设置的最长时间
- 示例1
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
class TestHogwarts():
def setup(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(5)
def test_wait(self):
def wait(x): # 必须传入1个参数
return len(self.driver.find_elements(By.XPATH, '//*[@class="table-heading"]')) >= 1
WebDriverWait(self.driver, 10).until(wait) # 不要写成wait()
self.driver.find_elements(By.XPATH, 'xx...').click()
- 示例2
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
class TestHogwarts():
def setup(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(5)
WebDriverWait(self.driver, 10).until(wait)
self.driver.find_elements(By.XPATH, 'xx...').click()
- 示例3
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
class TestHogwarts():
def setup(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(5)
def teardown(self):
self.driver.quit()
def test_wait(self):
WebDriverWait(self.driver, 10).until(expected_conditions.element_to_be_clickable(By.XPATH,'//xx'))
self.driver.find_elements(By.XPATH, 'xx...').click()
04 | web控件定位与常见操作
xpath定位
- 定位比css_selector慢,因为是从头到尾的遍历
- 可以应用于appium和selenium中
- 用法
| 表达式 | 结果 |
|---|---|
| /bookstore/book[1] | 选取属于bookstore子元素的第一个book元素 |
| /bookstore/book[last()] | 选取属于bookstore子元素的最后一个book元素 |
| /bookstore/book[last()-1] | 选取属于bookstore子元素的倒数第二个book元素 |
| /bookstore/book[position()<3] | 选取最前面的两个属于bookstore元素的子元素的book元素 |
| //title[@lang='eng'] | 选取所有title元素,且这些元素拥有值为eng的lang属性 |
| /bookstore/book[price>35.00] | 选取bookstore元素的所有book元素,且其中的price元素的值须大于35.00 |
| /bookstore/book[price>35.00]/title | 选取bookstore元素中的book元素的所有title元素,且其中的price元素的值须大于35.00 |
| 表达式 | 结果 |
|---|---|
| nodename | 选取此节点的所有子节点 |
| / | 从子节点中查找 |
| // | 从子孙节点中查找 |
| . | 选取当前节点 |
| .. | 选取当前节点的父节点 |
| @ | 选取属性 |
- 页面中的 id 和 name 都是唯一的
- 以百度为例,在谷歌浏览器console中
# // 选取所有元素
# * 选取任意元素
$x('//*[@id="s_tab"]//b') # 网页
$x('//*[@id="s_tab"]//a[1]') #资讯
$x('//*[@id="s_tab"]//a[last()]') # 更多
$x('//*[@id="s_tab"]//a[last()-1]') # 地图
$x('//*[@id="kw"]') # 输入框
css selector
- 移动端原生的应用不支持css selector(应用中嵌入h5除外)
- 比xpath定位快,css是通过样式定位,xpath是从头到尾遍历
- 用法
| 选择器 | 例子 | 例子描述 |
|---|---|---|
| .class | .intro | 选择class="intro"的所有元素 |
| #id | #firstname | 选择id="firstname"的所有元素 |
| * | * | 选择所有元素 |
| element | p | 选择所有<p>元素 |
| element,element | div,p | 选择所有<div>元素和所有<p>元素 |
| element element | div p | 选择<div>所有子孙元素中的<p>元素 |
| element>element | div>p | 选择<div>所有子元素中的<p>元素 |
| element+element | div+p | 选择紧接在<div>元素后的所有<p>元素 |
| [attribute] | [target] | 选择带有target属性所有元素 |
| [attribute=value] | [target=_blank] | 选择target="_blank"的所有元素 |
| :nth-child(n) | p:nth-child(2) | 选择<p>的父元素的第2个子元素 |
| element1~element2 | p~ul | 选择前面有<p>元素的每个<ul>元素 |
- 以百度为例,在谷歌浏览器console中
$('#kw')
$('[id=kw]')
$('[name=wd]')
$('#s_tab b') # 网页
$('#s_tab a:nth-child(2)')
$('#s_tab a:nth-last-child(1)')
05 | web控件交互
简介
- 官方文档:https://selenium-python.readthedocs.io/api.html
- ActionChains:执行PC端的鼠标点击、双击、右键、拖拽等事件
- TouchActions:模拟PC和移动端的点击、滑动、拖拽、多点触控等多种手势操作
ActionChains
-
执行原理:
1.调用ActionChains的方法时,不会立即执行,而是将所有的操作,按顺序存在一个队列里,当调用perform
()方法是,队列中的事件会依次执行 -
基本用法
1.生成一个动作action = ActionChains(driver)
2.动作添加方法1 action.方法1
3.动作添加方法2 action.方法2
4.调用perform()方法执行 (action.perform()) -
具体写法
1.链式写法:
ActionChains(driver).move_to_element(element).click(element).perform()
2.分布写法
actions = ActionChains(driver)
actions.move_to_element(element)
actions.click(element)
actions.perform()
- 练习
1.demo:http://sahitest.com/demo/
2.示例
from time import sleep
import pytest
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
class TestActionChains():
def setup(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(2)
self.driver.maximize_window()
def teardown(self):
self.driver.quit()
@pytest.mark.skip
def test_case_click(self):
element_click = self.driver.find_element_by_id("su")
action = ActionChains(self.driver)
action.click(element_click)
# action.context_click(element_click)
# action.double_click(element_click)
action.perform()
@pytest.mark.skip
def test_moveto(self):
self.driver.get("https://www.baidu.com")
shezhi = self.driver.find_element(By.XPATH, '//*[@id="u1"]/a[9]')
# shezhi = self.driver.find_element_by_link_text("设置")
action = ActionChains(self.driver)
action.move_to_element(shezhi)
action.perform()
sleep(3)
def test_dragdrop(self):
self.driver.get("http://sahitest.com/demo/dragDropMooTools.htm")
drag = self.driver.find_element_by_id("dragger")
drop1 = self.driver.find_element_by_xpath("/html/body/div[2]")
drop2 = self.driver.find_element_by_xpath("/html/body/div[3]")
drop3 = self.driver.find_element_by_xpath("/html/body/div[4]")
action = ActionChains(self.driver)
action.drag_and_drop(drag, drop1).pause(1)
action.click_and_hold(drag).release(drop2).pause(1)
action.click_and_hold(drag).move_to_element(drop3).release().pause(1)
action.perform()
sleep(3)
def test_key(self):
self.driver.get("http://sahitest.com/demo/label.htm")
username = self.driver.find_element_by_xpath("/html/body/label[1]/input")
username.click()
action = ActionChains(self.driver)
action.send_keys("xiaoming").pause(1)
action.send_keys(Keys.SPACE).pause(1)
action.send_keys("xiaohong").pause(1)
action.send_keys(Keys.BACK_SPACE).pause(1)
action.perform()
sleep(3)
def test_copy_paste(self):
self.driver.get("http://sahitest.com/demo/label.htm")
username1 = self.driver.find_element_by_xpath("/html/body/label[1]/input")
username2 = self.driver.find_element_by_xpath("/html/body/label[2]/table/tbody/tr/td[2]/input")
username1.send_keys("xiaoming")
username1.send_keys(Keys.CONTROL, 'a')
sleep(1)
username1.send_keys(Keys.CONTROL, 'c')
sleep(1)
username2.send_keys(Keys.CONTROL, 'v')
sleep(3)
TouchAction
- 参考资料:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.touch_actions
- ActionChains只是针对pc端程序鼠标模拟的一系列操作,对移动端H5页面操作是无效的;TouchAction可以对移动端H5页面操作,通过TouchAction可以实现点击、滑动、拖拽、多点触控、以及模拟手势等各种操作
- 手势控制
1.tap:单击
2.double_tap:双击
3.tap_and_hold:在指定元素上点击但不释放
4.move:移动指定偏离(未释放 )
5.release:释放手势
6.scroll:点击兵滚动
7.scroll_from_element:从某个元素位置开始点击并滚动(向下滚动为负数,向上滚动为正数)
8.long_press:长按
9.flick:手势滑动
10.flick_element:从某个元素位置开始滑动(向上滑动为负数,向下滑东为正数)
11.perform:执行
12.练习
from time import sleep
from selenium import webdriver
from selenium.webdriver import TouchActions
class TestTouchAction():
def setup(self):
option = webdriver.ChromeOptions()
option.add_experimental_option('w3c', False)
self.driver = webdriver.Chrome(options=option)
self.driver.maximize_window()
self.driver.implicitly_wait(3)
def teardown(self):
self.driver.quit()
def test_touchaction_scrollbotton(self):
self.driver.get("https://www.baidu.com")
el = self.driver.find_element_by_id("kw")
el_search = self.driver.find_element_by_id("su")
el.send_keys("selenium 测试")
action = TouchActions(self.driver)
action.tap(el_search)
action.scroll_from_element(el, 0, 1000)
action.perform()
sleep(3)
表单操作
- 表单定义
1.使用表单标签<form>定义的区域
2.允许用户在表单中(文本域、下拉列表、单选框、复选框)输入信息元素 - 练习
from time import sleep
from selenium import webdriver
class TestForm():
def setup(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(5)
self.driver.maximize_window()
def teardown(self):
self.driver.quit()
def test_form(self):
self.driver.get("https://testerhome.com/account/sign_in")
self.driver.find_element_by_id("user_login").send_keys("123")
self.driver.find_element_by_id("user_password").send_keys("password")
self.driver.find_element_by_xpath('//*[@id="new_user"]/div[4]/input')
sleep(3)
06 | 网页frame与多窗口处理
多窗口处理
- 多窗口识别:通过获取窗口的唯一标识“句柄”来识别,可理解为窗口的id
- 处理流程
1.driver.current_window_handle:先获取当前窗口的句柄
2.driver.window_handles:在获取所有窗口句柄
3.判断当前窗口是否是想要操作的窗口,若是则操作窗口,若不是则跳转另外的窗口进行操作(driver.switch_to_window) - 练习
# 1.打开百度
# 2.点击登录-注册,输入用户名、账号
# 3.返回登录页,点击登录
# 4.输入用户名、密码,点击登录
from time import sleep
from web.base import Base
class TestWindows(Base):
def test_window(self):
self.driver.get("https://www.baidu.com")
self.driver.find_element_by_link_text("登录").click()
print(self.driver.current_window_handle)
print(self.driver.window_handles)
self.driver.find_element_by_link_text("立即注册").click()
print(self.driver.current_window_handle)
print(self.driver.window_handles)
windows = self.driver.window_handles
self.driver.switch_to.window(windows[-1])
self.driver.find_element_by_id("TANGRAM__PSP_4__userName").send_keys("username")
self.driver.find_element_by_id("TANGRAM__PSP_4__phone").send_keys("13212121212")
sleep(3)
self.driver.switch_to.window(windows[0])
self.driver.find_element_by_id("TANGRAM__PSP_10__footerULoginBtn").click()
self.driver.find_element_by_id("TANGRAM__PSP_10__userName").send_keys("username")
self.driver.find_element_by_id("TANGRAM__PSP_10__password").send_keys("password")
self.driver.find_element_by_id("TANGRAM__PSP_10__submit").click()
sleep(3)
frame处理
- 在web自动化中,若一个元素定位不到,很可能是在frame中
- frame定义
1.frame是html中的框架,即在同一个浏览器中显示多个页面
2.基于html的框架,又分为垂直框架和水平框架(cols,rows) - frame分类
1.frame标签包含frameset、frame、iframe三种
2.frameset和普通的标签一样,不影响正常定位,可以使用index、id、name、webelement等任意方式定位frame
3.frame和iframe对selenium定位是一样的,selenium有一组方法对frame进行操作 - 示例
https://www.w3school.com.cn/tiy/t.asp?f=html_frame_cols
多frame切换
- frame存在两种:嵌套、未嵌套
- 切换frame
1.driver.switch_to.frame() 根据元素id或index切换frame
2.driver.switch_to.default_content() 切换到默认frame
3.driver.switch_to.parent_frame() 切换到父级frame - 处理未嵌套的iframe
1.driver.switch_to_frame("frame的id")
2.driver.switch_to_frame("frame-index") frame无id时依据索引处理,索引从0开始 driver.switch_to_frame(0) - 处理嵌套的iframe
1.先进入到iframe的父节点,再进到子节点,然后对子节点里的对象进行处理
2.driver.switch_to.frame("父节点")
3.driver.switch_to.frame("子节点") - 练习
from web.base import Base
class TestFrame(Base):
def test_frame(self):
self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
self.driver.switch_to.frame("iframeResult")
print(self.driver.find_element_by_id("draggable").text)
# self.driver.switch_to.default_content()
self.driver.switch_to.parent_frame()
print(self.driver.find_element_by_id("submitBTN").text)
07 | selenium多浏览器处理
传不同参数测试不同浏览器,用来做浏览器兼容性测试
import os
from selenium import webdriver
class Base():
def setup(self):
browser = os.getenv("browser")
if browser == "firefox":
self.driver = webdriver.Firefox()
elif browser == "chrome":
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(5)
self.driver.maximize_window()
def teardown(self):
self.driver.quit()
- 命令行运行:
browser=chrome pytest test_form.py,mac或linux执行此命令是可以的,我是window执行此命令是不可以的
08 | 执行JavaScript脚本
js的处理
- selenium能够执行js,js能做的事,selenium大部分也能做
- 直接使用js操作页面,能解决很多click()不生效的问题
- 页面滚动到底部、顶部
- 处理富文本、时间空间的输入
- 在chrome浏览器的console中执行js
document.title
window.alert("hello selenium")
JSON.stringify(performance.timing)
selenium中如何调用js
- execute_script:执行js
- return:返回js的返回结果
- execute_script:arguments传参
- 练习
# 滑动到浏览器底部或者顶部
# 1.打开百度页面
# 2.输入搜索关键字
# 3.点击搜索后,跳转到搜索结果页
# 4.滑动到底部点击 下一页
from time import sleep
from web.base import Base
class TestJS(Base):
def test_js_scroll(self):
self.driver.get("https://www.baidu.com")
self.driver.find_element_by_id("kw").send_keys("selenium测试")
self.driver.execute_script('return document.getElementById("su")').click()
sleep(3)
self.driver.execute_script("document.documentElement.scrollTop=2000")
self.driver.find_element_by_xpath('//*[@id="page"]/a[10]').click()
sleep(3)
# for code in ['return document.title', 'return JSON.stringify(performance.timing)']:
# print(self.driver.execute_script(code))
print(self.driver.execute_script('return document.title;return JSON.stringify(performance.timing)'))
处理时间控件
- 大部分时间空间都是readonly属性,自动化中对控件的操作可以使用js来操作
- 思路
1.取消日期的readonly属性
2.给value赋值
def test_datetime(self):
self.driver.get("https://www.12306.cn/index/")
self.driver.execute_script('a=document.getElementById("train_date");a.removeAttribute("readonly")')
self.driver.execute_script('document.getElementById("train_date").value="2020-12-20"')
sleep(3)
print(self.driver.execute_script('return document.getElementById("train_date").value'))
09 | 文件上传、弹框处理
文件上传
- input标签可以直接使用send_keys(文件地址)上传文件
from time import sleep
from web.base import Base
class TestFile(Base):
def test_file_upload(self):
self.driver.get("https://image.baidu.com/")
self.driver.find_element_by_xpath('//*[@id="sttb"]/img[1]').click()
self.driver.find_element_by_id("stfile").send_keys(r"C:\Users\xx\Desktop\截图.png")
sleep(3)
弹框处理
- 弹框处理机制
1.页面操作有时会遇到JavaScript生成的alert、confirm、prompt弹框,可以使用switch_to.alert()方法定位
2.然后使用text、accept、dismiss、send_keys等方法操作
3.分辨alert、window、div模态框 - 操作alert常用方法
1.switch_to.alert():获取当前页面上的警告框
2.text:返回alert、confirm、prompt中的文字信息
3.accept():模拟点击确认
4.dismiss():模拟点击取消
5.send_keys(keysToSend):发送文本至警告框
from time import sleep
from selenium.webdriver import ActionChains
from web.base import Base
class TestAlert(Base):
"""
1.打开网页 https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
2.拖拽元素A到元素B
3.此时会有alert弹窗,点击确定
4.点击运行
5.关闭网页
"""
def test_alert(self):
self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
self.driver.switch_to.frame("iframeResult")
drag = self.driver.find_element_by_id("draggable")
drop = self.driver.find_element_by_id("droppable")
action = ActionChains(self.driver)
action.drag_and_drop(drag, drop).perform()
sleep(3)
self.driver.switch_to.alert.accept()
self.driver.switch_to.default_content()
self.driver.find_element_by_xpath('//*[@id="submitBTN"]').click()
sleep(3)
10 | pageObject设计模式
参考资料
- 最先提出者:https://martinfowler.com/bliki/PageObject.html
- selenium官方文档:https://www.selenium.dev/documentation/en/guidelines_and_recommendations/page_object_models/
核心思想
image.png
- 把操作细节封装成方法,对外提供封装方法
- 只为主要元素进行封装
11 | PageObject原则
六大原则
- the public methods represent the services that the page offers(用公共方法代替页面提供的服务)
- try not to expose the internals of the page(尽量不要暴露页面内部细节)
- generally don't make assertions(通常不在PageObject里做断言)
- methods return other PageObjects(跳转其他页面时,要返回其他页面)
- need not represent an entire page(不要对页面中的每个元素建模)
- different results for the same action are modelled as different methods(相同动作的不同结果被建模为不同的方法)
12 | PageObject实战
内容较多,参考我在github上的代码https://github.com/tim8709/hogwarts_testing/tree/master/web/page_object
13 | web自动化测试实战一
参考资料
复用已有的浏览器(调试代码用)
- 开启chrome debug模式:
chrome -remote-debugging-port=9222 - 注:开启调试模式前,需关闭所有浏览器
- 示例
from selenium import webdriver
from selenium.webdriver.common.by import By
class TestDebug():
def setup(self):
# 声明一个变量,设置为chrometoptions
chrome_opts = webdriver.ChromeOptions()
# 此处的端口号要与chrome调试模式设置的端口号相同
chrome_opts.debugger_address = "127.0.0.1:9222"
self.driver = webdriver.Chrome(options=chrome_opts)
def teardown(self):
# self.driver.quit()
pass
def test_test(self):
# self.driver.get("https://home.testing-studio.com/")
self.driver.find_element(By.CSS_SELECTOR, ".ww_icon_AppMemberJoinBig").click()
使用cookie
def test_cookies(self):
# sleep(15)
# cookies = self.driver.get_cookies()
# with open("cookies.txt", "w") as f:
# json.dump(cookies, f)
with open("cookies.txt", "r") as f:
cookies = json.load(f)
for cookie in cookies:
if "expiry" in cookie.keys():
cookie.pop("expiry")
self.driver.add_cookie(cookie)
13 | web自动化测试实战二
PageObject原理
image.png
14 | web自动化测试实战三
参考代码
https://github.com/tim8709/hogwarts_testing/tree/master/web/selenium