课程目录
- selenium webdriver环境安装、原理
- 前端页面:html\dom对象
- 8大元素定位、xpath详解
- web常用元素操作
- PageObject模式应有、自动化用例设计
- 深入分层设计
- basepage页面提取
- pytest框架
- jenkins集成
- allure报告集成
web自动化环境安装
-
安装selenium
- 命令行安装
pip2 install --user selenium
- 命令行安装
-
安装浏览器和浏览器驱动
- chrome -- chromedriver :本次课程使用
- ie -- ieserverdriver
- firefox -- geckodriver
- chromedriver国内镜像网站:npm.taobao.org/mirrors/chr…
- chromedriver官方(需要梯子):sites.google.com/a/chromium.…
- web端:人、浏览器进行交互--> 代码通过驱动程序和浏览器进行交互
-
chromedriver存放的位置
-
安装在python安装目录下Scripts,则代码中写为:
driver=webdriver.Chrome() -
安装在其他目录下,则代码中写为:
driver=webdriver.Chrome("chromedriver的路径")
-
-
启动浏览器
from selenium import webdriver # 启动浏览器 driver=webdriver.Chrome("/Users/xxx/python_web_demo/chrome_driver/chromedriver")run一下这个.py文件,即可启动谷歌浏览器,如图:
web前端知识
自己有一些前端知识的基础,这里简单回顾,没学过html的还是要系统的看看
web页面组成
- HTML 定义页面呈现的内容,html是一门标记语言,不是变成语言
- CSS 控制页面如何布局、显示
- JavaScript 控制页面的行为
HTML标签对
- HTML标记标签通常被称为HTML标签(HTML tag)
- HTML标签由尖括号包围的关键字组成,如
<body> - HTML标签通常成对出现,如
<html>``</html> - 标签对中的第一个标签是开始标签,第二个标签是结束标签
- 开始和结束标签也叫开放标签和闭合标签
input标签
- 用于收集用户信息
- 根据不同的type属性值,输入字段拥有很多形式
- 输入字段可以是文本字段、复选框、单选框、复选框、提交按钮、掩码后的文本框等
HTML属性
- placeholder:text 规定帮助用户填写输入的字段
- readonly:规定输入字段为只读
- type:button/checkbox/file/password/radio/sumbit/text 规定input元素的类型
- checked:checked 规定此input元素首次加载时应当被选中
- disabled:disabled 当input元素加载时禁用此元素
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>python web自动化学习</title>
</head>
这是body标签的内容啦~~~
<p>这里会是一个段落</p>
输入框:<input></input><br>
按钮:<input type="submit"></input><br>
密码:<input type="password"></input><br>
爱好:<input type="checkbox">摄影</input><input type="checkbox">音乐</input><input type="checkbox">爬山</input>
<br>
单选:<input type="radio" name="sex">男</input><input type="radio" name="sex">女</input>
<br>
上传文件:
<input type="file">请上传图片</input>
<br>
按钮不可点击状态:
<input type="submit" disabled="disabled"></input><br>
百度地图:
<a href="map.baidu.com">百度地图点击一下</a>
<br>
百度logo:
<img src="https://www.baidu.com/img/bd_logo1.png?where=super" height="129" width="270">
<br>
下拉列表:
<select>
<option>---请选择---</option>
<option>周杰伦</option>
<option>林俊杰</option>
</select>
<br>
表单:
<form>
<table>
<tr >
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>4</td>
</tr>
</table>
</form>
<br>
iframe框:<br>
<iframe src="https://www.baidu.com" width="500" height="300"></iframe>
<br>
多文本域
<textarea>
</textarea>
<h1>hello world</h1>
<h2>hello world</h2>
<h3>hello world</h3>
<h4>hello world</h4>
<h5>hello world</h5>
<h6>hello world</h6>
</body>
</html>
JS-DOM对象
JavaScript
-
在html页面中写js,代码是要放在
<script></script>标签内 -
变量:
var 变量名 = 值
-
函数
function 函数名称(参数){ //代码块 } -
调用函数
函数名(参数)
DOM对象
- DOM(document object mode)是一套web标准:定义了访问html文档的一套属性、方法和事件
- 本质:网页与脚本语言沟通的桥梁。脚本语言通过DOM对象来访问html页面,从而改变文档的结构、样式和内容。当浏览器载入html文档,它就成为document对象。
- HTML DOM独立于平台和编程语言。它可以被任何编程语言诸如java、javascript和VBScript使用
DOM树
window:window对象表示浏览器中打开的窗口。Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问。
DOM对象查找元素
- 元素id属性:
document.getElementById("") - 元素的class属性:
document.getELementsByClassName("") - 元素的标签名属性:
document.getELementsByTagName("") - 元素的name属性:
document.getELementsByName("") - css选择器:
document.querySelector(css)
可以通过控制台console直接搜索结果:
元素属性
-
改变元素属性值
document.getByElementByXXX("").属性名=新属性值document.getByElementByXXX("").setAttribute("属性名","新的属性值") -
获取元素属性值
document.getByElementByXXX("").getAttribute("属性名") -
改变元素的内容
-
包含html元素标签 -- 有后代:
document.getByElementByXXX("").innerHTML=new HTML -
不包含html元素标签,纯文本:
document.getByElementByXXX("").innerText=new text
-
-
改变样式
document.getByElementByXXX("").style.样式名=样式值示例:
document.getByElementByXXX("").style.visibility="hidden"document.getByElementByXXX("").style.color="red"
事件
浏览器和用户事件 - 触发 -执行js代码带来不同的页面响应。
例如:点击事件、输入事件、鼠标事件
-
页面加载完成事件
window.onload=function(){ alert("everything is ready!") } -
点击事件
document.findElementById("xxx").onclick=function(){ alert("点了我一下") }
webdriver源码理解
- webdriver库是客户端,chromedriver是服务端
- 以http协议的形式约定了一套web自动化中的命令(command)
流程:
- webdriver启动chromedriver
- 发送http请求到chromedriver(server端),执行命令 (execute)
- chromedriver驱动chrome浏览器
对浏览器的基本操作
# encoding:utf-8
from selenium import webdriver
# 启动浏览器,开启与浏览器之间的会话
driver=webdriver.Chrome("/Users/xxx/Documents/000_Inbox/python_web_demo/chrome_driver/chromedriver")
# 访问一个网页
driver.get("http://www.baidu.com")
# 窗口最大化
driver.maximize_window()
# 访问另一个网页
driver.get("http://www.taobao.com")
# 回退到上一页
driver.back()
# 回到下一页
driver.forward()
# 刷新
driver.refresh()
# 获取标题
print driver.title
# 获取网址
print driver.current_url
# 获取窗口的句柄
print driver.current_window_handle
# 关闭当前窗口
driver.close()
# 关闭会话
driver.quit()
打印结果如下:
driver.close()和driver.quit()的区别:
driver.close()源码注释:Closes the current window. (关闭当前窗口)driver.quit()源码注释:Closes the browser and shuts down the ChromeDriver executable that is started when starting the ChromeDriver.(关闭浏览器及杀掉对应的chromedriver进程)
八大元素定位方式及xpath详解
八大元素定位方式
- id
- classname
- name
- tagname
- link_text
- partial_link_text
- xpath
- css
from selenium import webdriver
# 启动浏览器,开启与浏览器之间的会话
driver=webdriver.Chrome("/Users/leitianxiao/Documents/000_Inbox/python_web_demo/chrome_driver/chromedriver")
# 访问百度
driver.get("https://www.baidu.com")
# 方式一:通过id定位
ele=driver.find_element_by_id("kw") # 返回的是一个WebElement对象
print type(ele) # ele的类型: <class 'selenium.webdriver.remote.webelement.WebElement'>
print ele.get_attribute("class") # ele的class属性的值: s_ipt
# 方式二:通过classname定位
eles=driver.find_elements_by_class_name("s_ipt")
driver.find_element_by_class_name("s_ipt")
# 区别:通过classname定位可能不止一个元素,find_element返回第一个元素,find_elements返回所有元素
# 方式三:通过name定位
driver.find_element_by_name("wd")
driver.find_elements_by_name("wd")
# 方式四:通过tagname定位
driver.find_element_by_tag_name("input")
driver.find_elements_by_tag_name("input")
# 方式五、方式六:通过链接文本定位,针对链接,完全匹配和模糊匹配
# 完全匹配
driver.find_element_by_link_text("更多产品")
# 模糊匹配
driver.find_element_by_partial_link_text("产品")
# 方式七:通过xpath定位
# 绝对定位:
# 由树状根节点开始,以/开头,如: "/html/body/div[2]/div[2]/div[5]/div[1]/div/form/span[1]/input"
# 非常依赖页面的顺序和位置,开发稍微调整一下页面顺序,定位就失效了,一般不建议使用
driver.find_element_by_xpath("/html/body/div[2]/div[2]/div[5]/div[1]/div/form/span[1]/input")
# 相对定位
# 以//开头,不依赖于页面顺序和位置,只看有没有符合表达式的元素
driver.find_element_by_xpath("//input[@name='phone']") # "//标签名[@属性='属性值']",属性中可以使用逻辑运算
# 方式八:通过css定位
driver.quit()
xpath详解
-
利用标签内的属性进行定位
xpath="//标签名[@属性='属性值']",如://input[@name='phone']- 属性中可以进行逻辑运算,如
//input[@name='wd' and @class='s_ipt'] - F12-检查元素时,快捷ctrl+f呼出搜索框,用来检测这个xpath表达式结果是否是唯一
- 如果有一个完全一模一样的元素,从本级中无法区分,使用层级定位,从父级或者爷爷级开始定位,如:
//div[@id='u1']/a[@name='tj-login']
- 属性中可以进行逻辑运算,如
-
利用text()方法定位
xpath="//标签名[text()='文本内容']",如://div[@id='u_sp']/a[text()='贴吧'] -
利用contains()方法定位,也叫模糊定位
xpath = "//标签名[contains(@属性,'属性值')]",如//div[@id='u_sp']/a[contains(@href, 'tieba')]xpath = "//标签名[contains(text(),'文本内容')]" -
轴定位
示例:如蛋卷基金https://danjuanapp.com/,它的每个“查看组合详情”的代码都一模一样,较难定位。
使用轴定位:
//h1[text()='银行螺丝钉组合']/ancestor::a/descendant::a//h1[text()='九雾组合']/ancestor::a/descendant::a//h1[text()='久聪定投']/ancestor::a/descendant::a
等待方式
在实际项目中,经常会报错,noSucElement,可能存在问题的原因是:
- 定位表达式有问题
- 没有设置等待时间,元素还没有加载出来
- 切换问题,可能是在另外一个html中,如iframe
解决第二个问题,需要使用到等待时间。有三种等待方式:(app通用)
什么时候要等待元素加载:但凡操作引起了页面的变化,都要进行等待,如跳转到其他页面,弹出弹窗等....
-
强制等待
sleep(秒)固定等待
-
隐式等待
implicitly_wait(秒)设置最长等待时间,在这个时间内加载完成,则执行下一步。整个driver的会话周期内,设置一次即可,全局可用。这个时间内没有加载完成,抛出timeout异常
-
显式等待
明确等到某个条件满足之后,再去执行下一步。
程序每隔xx秒看一眼,如果条件成立,则执行下一步,否则继续等待,直到超过设置的最长时间,然后抛出timeout异常。
WebDriverWait类:显性等待类
`WebDriverWait(driver,等待时长,轮循周期).until()/until_not()`expected_conditions模块:提供了一系列期望发生的条件。
常用条件:
- presence_of_element_located:元素存在,一个元素出现页面的dom中,但不一定可见。
- visibility_of_element_located:元素可见,一个元素出现页面的dom中,且可见,height wight不为0.
- element_to_be_clickable:元素可点击
使用显式等待:
-
引入相关的库
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common by import By -
使用
-
先确定元素的定位表达式
web_locator='xxx' -
调用WebDriverWait类设置等待总时长、轮询周期,默认检测频率为0.5s。并调用until、until_not方法
WebDriverWait(webdriver,等待总时长,轮询周期).until(判断条件) -
使用expected_conditions对应的方法来生成判断条件
EC.类名((定位方式,定位表达式))
示例:
# encoding:utf-8 from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 启动浏览器,开启与浏览器之间的会话 driver=webdriver.Chrome("/Users/xxx/Documents/000_Inbox/python_web_demo/chrome_driver/chromedriver") # 设置隐式等待,全局等待,设置一次即可 # driver.implicitly_wait(20) # 访问百度 driver.get("https://www.baidu.com") # 等待 "登录" 元素存在 locator=(By.XPATH,"//*[@id='u1']/a[@name='tj_login']") WebDriverWait(driver,20,1).until(EC.presence_of_element_located(locator)) # 点击登录 driver.find_element_by_xpath("//*[@id='u1']/a[@name='tj_login']").click() # 等待 "用户名登录" 元素存在 WebDriverWait(driver,20,1).until(EC.presence_of_element_located((By.ID,"TANGRAM__PSP_10__footerULoginBtn"))) # 点击 用户名登录 driver.find_element_by_id("TANGRAM__PSP_10__footerULoginBtn").click() driver.quit() -
切换方式
切换iframe
iframe是另一个html页面,要定位iframe框中的元素,需要切换到iframe。
怎么知道这个元素是不是在iframe框中,技巧:
-
使用
driver.switch_to.frame切换iframe框三种示例:
driver.switch_to.frame('frame_name')driver.switch_to.frame(1)driver.switch_to.frame(driver.find_elements_by_tag_name("iframe")[0])以虫部落为例:
from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 启动浏览器,开启与浏览器之间的会话 driver=webdriver.Chrome("/Users/xxx/Documents/000_Inbox/python_web_demo/chrome_driver/chromedriver") # 设置隐式等待,全局等待,设置一次即可 driver.implicitly_wait(10) # 访问百度 driver.get("https://search.chongbuluo.com/") # 等待 "百度" 元素存在 locator=(By.XPATH,"//li[@id='baidu']") WebDriverWait(driver,20).until(EC.presence_of_element_located(locator)) # 点击百度 driver.find_element_by_xpath("//li[@id='baidu']").click() # 切换到iframe框,进入了另外一个html页面 driver.switch_to.frame("engine") WebDriverWait(driver,20).until(EC.visibility_of_element_located((By.XPATH,"//input[@id='kw']"))) s1="定位到了吗" s1=(unicode(s1, 'utf-8')) # 定位输入框 driver.find_element_by_xpath("//input[@id='kw']").send_keys(s1) # 点击搜索 driver.find_element_by_id("su").click() driver.quit() -
通过EC条件中的
frame_to_be_available_and_switch_to_it切换iframe框EC.frame_to_be_available_and_switch_to_itiframe是否可用且切换进入iframe使用这个就不需要再使用switch_to了
示例:
WebDriverWait(driver,20).until(EC.frame_to_be_available_and_switch_to_it("engine")) -
从iframe框切换到默认主页面
driver.switch_to.default_content()driver.switch_to.parent_frame()切换到父级页面,多层iframe使用
切换窗口
通过driver.switch_to.window()切换窗口
# step1.获取窗口的总数以及句柄,新打开的窗口排在最后
handles=driver.window_handles
print handles
print driver.current_window_handle # 打印当前句柄
# step2.切换到最后一个句柄,也就是最新打开的句柄
driver.switch_to.window(handles[-1])
# 再对切换到的页面进行元素定位和操作
# ...
EC条件中EC.new_window_is_opened(handles)判断新窗口是否打开,此处的handles应该在引起窗口数量增加之前获取,要使用driver.switch_to.window()切换到新的句柄,需要再获取一次handles
切换alert (alert不是html元素)
- 通过
driver.switch_to.alert切换alert
alert=driver.switch_to.alert
alert.accept() # 确认
alert.dismiss() # 取消
alert.text # 获取alert中的文本
- 通过EC类
EC.alert_is_present()判断alert是否出现
·第90课
PageObject 页面对象模型
PageObject,页面对象。UI自动化的操作,都是在各个页面上进行操作,不同的页面的功能串起来。页面是有限的,把所有页面封装成对象,每个页面封装为一个对象(类),把每个页面的功能进行封装。按业务逻辑调用各个页面对象中的功能(函数)
原理实现:
将页面的元素定位和元素行为封装成一个page类
类的属性:元素的定位
类的行为:元素的操作
实现页面对象和测试用例分离
测试用例: 调用所需页面对象中的行为,组成测试用例。
好处:
- 当某个页面发生改变时,只需要修改该页面对象中的代码即可,不需要修改测试用例。
- 提高代码重用率。结构清晰,维护代码更容易
- 测试用例发生变化时,不需要或者需要修改少数页面对象的代码即可。
以登录页面为例:
项目结构:
项目名
- page_object
- login_page.py --登录页
- index_page.py --首页
- test_case
page_object/login_page.py:
# page_object/login_page.py
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self,driver):
self.driver=driver
'''
测试用例是调用多个页面类,在用一个浏览器会话上面串行执行,不应该在页面对象中打开浏览器,
否则每个页面对象都打开一个浏览器,而是在测试用例中打开一个浏览器会话。所以只能等待测试用例传参driver
'''
def login(self,phone,passwd,remeber_user=False):
WebDriverWait(self.driver,20).until(EC.visibility_of_element_located((By.XPATH,"//input[@name='phone']")))
# 输入手机号
self.driver.find_element_by_xpath("//input[@name='phone']").send_keys(phone)
# 输入密码
self.driver.find_element_by_xpath("//input[@name='password']").send_keys(passwd)
# 判断remeber_user的值,是否勾选记住密码
# 点击登录
self.driver.find_element_by_xpath("//button[text()='登录']").click()
def register_enter(self):
pass
# 点击注册
def forget_password(self):
pass
# 点击忘记密码
test_case/test_login.py
class TestLogin(unittest.TestCase):
def setUp(self):
self.driver=webdriver.Chrome("chromedriver路径")
self.driver.get("测试环境地址")
self.lg=LoginPage(self.driver)
def tearDown(self):
self.driver.quit()
# 正常用例:登录成功
def test_login_success(self):
# 前置: 访问登录页面 (使用def setUp(self))
# 步骤: 输入手机号、密码、登录
self.lg.login("18019230000","123456")
# 断言: 首页中能否找到属于首页的某个元素
# 等待10秒,判断首页的元素是否出现/存在,如果没有出现,可以说明没有跳转成功,相当于断言失败,但是不能直接写WebDriverWait(....)判断首页某个元素是否出现/存在,这样,当首页的元素进行修改或变动时,所有的测试用例都要改,没有真正做到po设计模式。
self.assertTure(IndexPage(self.driver).isExist_logout_ele())
pass
# 异常用例:手机号错误
def test_login_userphone_wrong(self):
# 前置: 访问登录页面
# 步骤: 输入手机号、密码、登录
self.lg.login("18019230001","123456")
# 断言: 登录页面提示:手机号错误
pass
# 异常用例:手机号为空
def test_login_userphone_null(self):
self.lg.login("","123456")
pass
page_object/index_page.py
class IndexPage:
def __init__(self.driver):
self.driver=driver
# 检查退出元素是否存在
def isExist_logout_ele(self):
# 如果存在返回Ture ,如果不存在返回False
try:
WebDriverWait(....).unitl
return Ture
except:
return False