python+selenium自动化
自动化脚本编写原则:LOVE原则
L:Locate,定位
O:Operate,操作
V:Verify,断言
E:Except,异常处理
注:python自带库第三方库是使用pip install 包名 安装的,
安装完成后都会在\Lib\site-packages文件夹下面
1. 安装工具
1-1.安装selenium
在DOS窗口执行: pip install selenium
pip install selenium -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
检查是否安装成功:pip show selenium
如果安装失败,执行命令:pip install -U selenium
pip install --user -U selenium
1-2.安装浏览器,注意浏览器与驱动版本要对应
Chrome(不区分64/32位)驱动:https://npm.taobao.org/mirrors/
火狐版本及驱动映射:https://github.com/mozilla/geckodriver/releases/
1-3.将下载好的压缩包解压后chromedriver.exe放到python安装路径下的Scripts文件夹下
如 C:\Program Files\Python36\Scripts
1-4.编写脚本,验证配置是否正确
from selenium import webdriver
dr = webdriver.Chrome() #获取浏览器句柄
url = "https://www.baidu.com/"
dr.get(url)
2. 打开网页如百度首页,打开F12,使用元素拾取器,标记输入框,可看到元素及CSS样式
3. html标签:
input代表输入框或按钮
a代表超链接
4. 八大元素定位方法
4-1.根据id属性定位:
find_element_by_id(id的值),使用条件是id的值必须为唯一,可查源码搜索
4-2.根据name属性定位:
find_element_by_name(name的值),使用条件是name的值必须为唯一
4-3.根据class属性定位:
find_element_by_class_name(class的值),使用条件同上
4-4.根据超链接文本定位:
find_element_by_link_text(超链接文本信息)
4-5.根据超链接部分文本信息定位:
find_element_by_partial_link_text(部分文本信息)
4-6.根据标签定位:
find_element_by_tag_name(标签名称) 较少使用
4-7.根据xpath定位:
4-7-1.根据绝对路径定位:
find_element_by_xpath('/html/body/div[5]/div/span[2]/input')
4-7-2.根据元素的单个属性定位:
find_element_by_xpath('//input[@type="submit"]')
4-7-3.根据元素的多个属性定位:
find_element_by_xpath('//*[@name="wd" and @class="s_ipt"]')
4-7-4.根据层级+元素属性定位:
find_element_by_xpath('//span[1]/input[@id="kw"]')
4-8.根据css定位:find_element_by_css_selector('css的语法') 定位速度快
4-8-1.根据id属性定位 ('#id')
4-8-2.根据class 属性定位 ('.class') 或 ('.class1.class2')
如果使用2个class定位失败,可以class1和class2中间加逗号
4-8-3.根据id和class之外的属性定位
('标签[属性="值"]')或 ('[属性="值"]') 或 ('[属性1="值1"][属性2="值2"]')
4-8-4.根据层级+属性定位 (父层级>子层级[属性="值"])
('span > input') 或 ('span > input#kw')
或('span.bg.s_ipt_wr.quickdelete-wrap > input')
或('span[name="goods_id"] > input')
问:有些元素,在谷歌浏览器上能定位,在火狐浏览器上定位失败,是什么原因呢?
因为不同浏览器的内核不一样,他们的CSS样式不一样。
5. 浏览器元素的操作
5-1. click() 点击
5-2. send_keys() 向文本框输入内容
5-3. clear() 清空文本框内容
5-4. quit() 退出浏览器
5-5. send_keys() 上传文件 ,只能在标签为input的元素中使用
5-6. text 获取元素文本信息
如获取右边input标签的“地图”: <input id="mapid"> 地图 </input>
map = dr.find_element_by_id("mapid")
print(map.text)
5-7. get_attribute('属性名称') 获取元素属性的值
5-8. sleep() 固定等待
5-9. implicitly_wait() 隐式等待,只对webdriver开头的操作生效
dr.implicitly_wait()
5-10. submit() 提交表单,模拟回车键的操作
5-11. maximize_window() 最大化窗口 dr.maximize_window()
5-12. is_displayed() 判断元素是否在页面显示,(肉眼可见的元素才能操作)
print(sBtn.is_displayed()) → True
5-13. 鼠标悬停、动作生效:
from selenium.webdriver import ActionChains # 导包
move_to_element() # 将鼠标移动到对应的元素
perform() # 执行所有ActionChains中存储的行为,即让悬停的动作生效
例:找到百度首页的“更多”,鼠标移过去展开“更多”
from selenium.webdriver import ActionChains # 导包
moreBtn = dr.find_element_by_name('tj_briicon') # 定位首页元素
ActionChains(dr).move_to_element(moreBtn).perform() #鼠标悬停生效
5-14. 多表单切换:
dr.switch_to.frame(切换到的表单) # 切换表单
dr.switch_to.default_content() # 切回默认表单
某些页面(如https://mail.163.com/)采用了iframe页面嵌套,即HTML里面嵌套了
另一个HTML,元素定位方法默认定位外层的HTML,
如果定位里面的HTML,需要进行表单切换
例:在网易邮箱的用户名输入内容
email = 'https://mail.163.com/'
dr.get(email)
# 定位iframe
frame = dr.find_element_by_xpath('//div[@id="loginDiv"]/iframe')
dr.switch_to.frame(frame) # 切换到表单2
username = dr.find_element_by_name('email')
username.send_keys('iframe定位')
多表单切换顺序为默认表单 → 表单2 → 默认表单 → 表单3
切回默认表单:
dr.switch_to.default_content() # 切回默认表单1
dr.switch_to.frame(‘表单3’) # 切换到表单3
5-15. 窗口切换
windows = dr.window_handles # 获取所有打开的窗口
# print(len(windows)) # 打印列表长度
dr.switch_to.window(windows[1]) # 切换到第2个窗口(取列表第2个元素)
5-16. 关闭窗口 注:驱动退出或窗口关闭后,该驱动无法继续使用
dr.close() # 关闭当前窗口, 若关闭多个窗口,需要先切换窗口
dr.quit() # 关闭所有窗口,退出驱动
5-17. 警告框处理
# dr.switch_to.alert.text # 获取警告框文本信息
# dr.switch_to.alert.accept() # 点击警告框的确认按钮
# dr.switch_to.alert.dismiss() # 点击警告框的取消按钮
例:百度首页→设置→搜索设置→确定后,处理警告框
url = "https://www.baidu.com/"
dr.get(url)
dr.maximize_window()
# 定位页面右上角的设置
setting = dr.find_element_by_css_selector('#s-usersetting-top')
# 鼠标悬停(需要先导包)
ActionChains(dr).move_to_element(setting).perform()
time.sleep(0.5)
# 点击“搜索设置”
dr.find_element_by_link_text("搜索设置").click()
time.sleep(0.5)
# 点击“保存设置”
dr.find_element_by_link_text("保存设置").click()
# 获取警告框文本信息
text = dr.switch_to.alert.text
print(text)
# 点击警告框的确认按钮
dr.switch_to.alert.accept()
5-18. 下拉框元素定位
# from selenium.webdriver.support.ui import Select # 导包
# Select(下拉框元素).select_by_index(下标值)
# Select(下拉框元素).select_by_value(value属性的值)
# Select(下拉框元素).select_by_visible_text(可见的文本值)
例:
from selenium.webdriver.support.select import Select # 导包
url = r"C:\下拉列表&只读元素&隐藏元素&滚动条练习.html"
dr.get(url)
ele = dr.find_element_by_css_selector('[id="s1"]')
Select(ele).select_by_value("br2") # 定位value属性的值
sleep(1)
Select(ele).select_by_index(4) # 定位0开始的第5个下拉框
sleep(1)
Select(ele).select_by_visible_text("selenium3") # 定位可见的文本值
注:下拉框元素定位,也可以先定位到下拉框,再选择元素进行操作(如点击)
5-19. js定位
在某些特殊情况下,浏览器的元素是无法被定位,或无法被操作的,
比如浏览器的滚动条,元素具有readonly属性,我们需要使用稍微
有难度一点的定位方法---调用js脚本进行定位。
5-19-1. 滚动条处理
# location1 = 'window.scrollTo(0,2000)'
# dr.execute_script(location1)
例:
url = r"C:\下拉列表&只读元素&隐藏元素&滚动条练习.html"
dr.get(url)
time.sleep(1)
location1 = 'window.scrollTo(0,500)'
dr.execute_script(location1)
time.sleep(1)
location2 = "window.scrollTo(0,1700)"
dr.execute_script(location2)
time.sleep(1)
location3 = "window.scrollTo(0,700);"
dr.execute_script(location3)
5-19-2. 若元素包含readonly属性,无法直接操作,
需要调用JS脚本把readonly属性删掉。
例:
url = r"C:\下拉列表&只读元素&隐藏元素&滚动条练习.html"
dr.get(url)
time.sleep(1)
# read_inputBox = """document.querySelectorAll("#s2")[0].removeAttribute("readonly")"""
# read_inputBox = 'document.querySelectorAll("#s2")[0].removeAttribute("readonly")'
read_inputBox = 'document.querySelectorAll(\'[id="s2"]\')[0].removeAttribute("readonly")'
dr.execute_script(read_inputBox)
inputBox = dr.find_element_by_css_selector("#s2")
inputBox.send_keys("这是删除元素属性的")
例:移除活动开始时间的readonly属性
js = '''document.querySelectorAll('[name="start_time"]')[0].removeAttribute("readonly")'''
# js = "document.querySelectorAll('#start_time_id')[0].removeAttribute('readonly')" # 也可使用id定位
js = 'document.querySelectorAll(\'[name="start_time"]\')[0].removeAttribute("readonly")' # 也可转义符
dr.execute_script(js)
startTime = dr.find_element_by_css_selector('[name="start_time"]')
startTime.clear()
import time # 导包一般放在前面
now = time.strftime('%Y-%m-%d %H:%M:%S') # 当前日期、时间
startTime.send_keys(now)
例:移除活动开始时间的readonly属性
js = "document.getElementById('end_time_id').removeAttribute('readonly')"
dr.execute_script(js)
endTime = dr.find_element_by_id('end_time_id')
endTime.clear()
from datetime import date, timedelta # 导包一般放在前面
end = date.today() + timedelta(10) # 日期是当前日期加10天
end2 = str(end) + ' 23:59:59' # 日期转换成字符串格式,再拼接上具体时间
endTime.send_keys(end2)
6. PO设计模式
全称是Page Object,是一种自动化测试的设计模式,它将元素定位、业务操作和测试用例分开。
这样的好处是,当页面元素发生变化,只需要修改存放改页面元素的类即可,大大减少了脚本的维护成本。
7. 单元测试框架
Python单元测试框架有unittest、pytest
单元测试框架(framework),是为了解决某一类问题,而设计出来的工具。
现状: a. 线性脚本
b. 当测试用例达到量级,没有代码的组织管理
c. 测试用例达到成百上千条的时侯、维护困难
d. 没有日志用例总数、通过数、失败数量、环境信息
e. 没有丰富的断言方法
# unittest总结:
# 1. setup()、teardown(),每条用例执行前后都要运行一次,适用于一条用例,多条用例耗费时间
# 2. setUpClass()、tearDownClass(),所有用例执行前后总共运行一次,适用于批量执行用例
# 3. 测试用例的执行顺序依据用例名称顺序执行
# 4. 自动化测试脚本必须要有环境恢复(删除存入数据库的内容等)
# 5. 环境恢复是在用例执行结束后,但自动化脚本的环境恢复代码可以紧跟在环境预置之后,不影响执行顺序
# 6. 在同一个类里面,如果方法里面的变量需要被其他方法使用,在定义变量的时候,变量名前加上self.
# 7. unittest.main()方法只会运行当前脚本中的测试用例(也就是方法名以test开头的)
import unittest
class MyTest(unittest.TestCase):
# pass
# 环境预置
# def setUp(self):
# print('环境预置')
@classmethod # 使用setUpClass必须声明装饰器
def setUpClass(self): # 批量执行用例时使用
print('环境预置')
# 测试用例必须是test开头
def testCase_1(self):
# 期望结果
expectResult = 'hello'
actualResult = 'hello world'
print('测试用例的操作步骤1')
# 断言
self.assertIn(expectResult,actualResult)
def testCase_2(self):
# 期望结果
expectResult = 123
print('测试用例的操作步骤2')
# 断言
self.assertEqual(expectResult,123,msg = "不相等")
# 恢复环境
# def tearDown(self):
# print('恢复环境')
@classmethod # 使用tearDownClass必须声明装饰器
def tearDownClass(self):
print('恢复环境')
if __name__ == '__main__':
unittest.main()
9. 项目实战 ecshop-夺宝奇兵
ecshop自动化 # 项目文件夹
public # 存放公共部分
public.py # 如登录等
reports # 存放生成的测试报告
testCases # 存放测试用例
test_01_duobao.py # 用例文件以test_开头
myDB # 操作数据库
myDB.py
HTMLTestRunner.py # 生成报告的模块
runAllCases.py # 执行所有用例,并发送邮件
1.项目环境搭建:
1.解压xapmm到C盘根目录,打开C:\xampp\xampp-control.exe,启动Apache和MySQL服务
2.如果Apache服务启动失败,修改C:\xampp\apache\conf\httpd.conf 里面的端口(默认为80,假设改为8000),
修改C:\xampp\apache\conf\extra\httpd-ssl.conf 里面的端口(默认443,假设改为4433)
3.如果MySQL服务启动失败,修改C:\xampp\mysql\bin\my.ini 里面的端口(默认3306)
4.将upload.zip解压后的文件夹改名为ecshop,移动到C:\xampp\htdocs 里面。
5.查看本机IP(假如为192.168.0.110),浏览器打开 192.168.0.110:8000/ecshop,根据页面提示安装即可
6.安装时注意:数据库搜一下选择test后覆盖,数据库用户名默认root,密码默认为空,页面底部注意安装测试数据
7.连接mysql时,如果报1130错误,需要在C:\xampp\mysql\bin\my.ini 里面,
[mysqld]下面增加一句话:skip-grant-tables
如果报1045-Access denied for user 'root'@'localhost'错误,需要打开DOS窗口,切换到C:\xampp\mysql\bin,
先输入mysql -u root -p或mysql -uroot登录,
再输入grant all privileges on *.* to 'root'@'%' identified by '123456'; 表示可以给所有ip都设定root登陆了,
其中%也可替换成本机ip或localhost,123456也可以改成别的密码
最后输入flush privileges; 刷新授权,之后重启mysql服务(win需要到任务器才能彻底关闭)
https://blog.csdn.net/qq_42036869/article/details/84671466
https://blog.csdn.net/tspangle/article/details/37761147
10. 自动化扩展知识点
1. 对验证码的常见处理方式?
去掉验证码,
设置万能验证码,
用python调用OCR模块,
调用第三方平台提供的接口进行识别,比如:斐斐打码,尖叫数据这些平台接口
2. WebDriver原理
浏览器的驱动,接收客户端发过来的指令(指令就是我们的脚本),
浏览器的驱动根据接收到的指令,驱动浏览器工作。
3. 如何提高selenium脚本的执行速度?
1)、提高网速;
2)、少用sleep,多用隐式等待或显式等待。
4. 自动化的应用范围和测试用例设计
1)、自动化测试,适用于回归测试,在功能测试之后进行。
2)、自动化测试,适用于项目周期长,版本多,界面元素稳定的项目。
3)、自动化测试用例的流程不要太长,不要有太多分支。
4)、自动化测试脚本是把手工测试过的用例转换成脚本,不是所有手工用例都能转化成脚本的,如UI测试的用例,
所以每个项目都有自己的自动化脚本转换率(我以前做过的项目,自动化脚本转换率要在40%以上)
5)、自动化测试脚本,要包含四个部分:
5.1 预置条件的构造。比如,你要查询某一天的数据,就要先在环境中构造这些数据;
5.2 操作步骤。
5.3 结果验证。(加入断言或验证,loadrunner里叫检查点)
5.4 环境恢复。把执行这条脚本所用到的数据全部删掉,把环境恢复到初始状态,以 免影响后续程序的执行
5. webdriver的工作流程(了解)
1.WebDriver启动目标浏览器,并绑定到指定端口。该启动的浏览器实例,做为webdriver的remoteserver.
2.Client端通过CommandExcuter发送HTTPRequest给remoteserver的侦听端口通信协议:thewebriverwireprotocol)
3.Remoteserver需要依赖原生的浏览器组件(如:lEDriverServer.exe、chromedriver.exe),来转化转化浏览器的native调用。