python语言的selenium自动化功能测试
自动化工具介绍
- UFT 非开源
- RobotFramework
- 高度扩展
- 拥有大量的库
- 支持关键字驱动
- Selenium
- 业内普及
- 灵活
- 使用场景广泛
为什么要进行自动化测试?
- 目的
- 解决回归测试、压力测试、兼容性测试
- 提高测试效率保证产品质量
- 相关解释
- 回归测试:项目在发布新版本之前进行的功能验证
- 压力测试:可以理解多用户去操作软件,统计服务器处理用户请求的能力
- 兼容性测试:表示在不同浏览器下软件运行状态
- 如下三种情况一般需要使用自动化?
- 需求变动不频繁
- 项目周期长
- 项目需要回归测试
配置编程环境
- window下安装python3.7(傻瓜式安装)
- Linux下安装python3.7
- 安装selenium包 pip3 install selenium
- 下载谷歌、火狐、IE驱动
- 安装pycharm
selenium的使用
selenium安装、卸载以及查看命令
- 安装 pip install selenium
- 卸载 pip uninstall selenium
- 查看 pip show selenium
元素的定位
- id定位 find_element_by_id("kw") 注:有些id值动态变化
- class_name定位 find_element_by_class_name("s_ipt") 注:classname有可能重复
- tag_name定位 find_element_by_tag_name("input") 注:tagname最容易重复
- name定位 find_element_by_name("wd") 注:name有可能重复
- link文字精确定位 find_element_by_link_text("登录")
- link文字模糊定位 find_element_by_partial_link_text("登")
- CSS定位(常用定位策略) 在selenium中推荐使用css定位,因为它比xpath定位速度快
- id选择器 #id值
- class选择器 .class值
- 元素选择器 标签名 >表示直接子级 空格表示子级
- p[id="user"]>input 表示p元素(id为user)的直接子级 或者使用p#user>input
- p[id="user"] input 表示p元素(id为user)的子级 或者使用p#user input
- css延伸
- input[type^="p"] type属性以p字母开头的元素
- input[type$="d"] type属性以d结尾的元素
- input[type*="w"] type属性包含w字母的元素
- XPath定位的常规策略
- 路径
- 绝对路径 绝对路径对页面结构要求非常高,不建议使用
- 相对路径 匹配任意层级的元素,不限制元素的位置
- 相对路径以//开始
- 格式 //input或者//*
- 路径结合属性 //input[@placeholder="密码A"]
- 路径结合逻辑(多个属性)
- //input[@placeholder="密码A" and @name="password"]
- //p[@id="parent"]/input[@placeholder="密码A" and @name="password"
- 路径结合层级
- xpath的延伸
- //*[text()="xxx"] 文本内容是xxx的元素
- //*[contains(@attribute,"xxx")] 属性中含有xxx的元素
- //*[starts-weith(@attribute,"xxx")] 属性以xxx开头的元素
- 路径
- 定位一组元素
- 通过下标操作定位元素 driver.find_elements_by_tag_name("input")[0].send_keys("吴鹏") 向一组input元素的第一个input输入内容
- 通过遍历操作定位元素 for el in elements:→el.send_keys("吴鹏")
元素操作方法
- 为什么要学习操作元素的方法?
- 需要让脚本模拟用户给指定元素输入值
- 需要让脚本模拟人为删除元素的内容
- 需要让脚本模拟点击操作
- 相关元素的操作方法
- send_keys() 扩展可以实现上传的操作(send_keys("文件路径"))
- click()
- clear() 清空
- maximize_window() 窗口最大化
- set_window_size(width,height) 设置浏览器窗口大小
- set_window_position(x,y) 设置浏览器窗口位置
- back() 后退,模拟浏览器后天按钮
- forward() 前进,模拟浏览器前进按钮
- refresh() 刷新,模拟浏览器F5刷新
- close() 关闭当前窗口,模拟电机架浏览器关闭按钮
- quit() 关闭浏览器驱动对象,关闭所有程序启动的窗口
- title 获取页面title
- current_url 获取当前页面URL
获取元素信息的常用方法
- size 返回元素大小
- text 返回元素的文本
- get_attribute("xxx") 获取属性值,传递的参数为元素的属性名
- is_displayed() 判断元素是否可见
- is_enabled() 判断元素是否可用
- is_selected() 判断元素是否选中,用来检查复选框或单选按钮是否被选中
鼠标操作
- 对于鼠标操作需要导入ActionChains包 from selenium.webdriver.common.action_chains import ActionChains
- 鼠标事件常用的操作方法 初始化 action=ActionChains(driver)
- context_click() 右击 action.context_click(driver.find_element_by_css_selector("#user")).perform()
- selenium框架中虽然提供了右击鼠标方法,但是没有提供选择右击菜单方法,可以通过发送快捷键的方式解决(经过测试,谷歌浏览器不支持)
- double_click() 双击 action.double_click(driver.find_element_by_css_selector("#user").send_keys("admin")).perform()
- drag_and_drop() 拖拽 action.drag_and_drop(第一个元素,第二个元素).perform()
- action.drag_and_drop_by_offset(source,xoffset=360,yoffset=180).perform()
- move_to_element() 悬停 action.move_to_element(driver.find_element_by_css_selector("button")).perform()
- perform() 执行以上事件方法
- context_click() 右击 action.context_click(driver.find_element_by_css_selector("#user")).perform()
键盘操作
selenium中把键盘的案件都封装在keys类中,导包 from selenium.webdriver.common.keys import keys
- 删除 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.BACK_SPACE) 单键就一个参数
- 全选 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.CONTROL,"a") 组合键两个参数
- 复制 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.CONTROL,"c")
- 粘贴 driver.find_element_by_css_selector("#userA").send_keys("admin1").send_keys(Keys.CONTROL,"v")
元素等待
元素等待类型
- 隐式等待
- 什么是隐式等待? 定位元素时,如果能定位到元素则直接返回该元素,不触发等待,如果不能定位到该元素,则间隔一段时间再去定位如果超出,则抛出such**异常
- 方法 driver.implicitly_wait(timeout)
- 显式等待
- 什么是显式等待? 定位指定元素时,如果能定位到元素则直接返回该元素,不触发等待,如果不能定位到元素,则隔一段时间再去定位,如果超出,跑出time**异常
- 实现方式
- 导包 from selenium.webdriver.support.wait import WebDriverWait
- 调用方法 until(method):直到...时 一般使用匿名函数来实现
- element=WebDriverWait(driver,10,1).until(lambda x:x.find_element_by_id("useA"))
操作API
下拉框
- 使用css去定位下拉框 如果option选项没有value值得话,css定位或者其他定位就不太方便
- 使用Select from selenium.webdriver.support.select import Select
- 通过下标 Select(driver.find_element_by_css_selector("#userA")).select_by_index(1)
- 通过value值 select_by_value(" ")
- 通过显示文本 select_by_visible_text(" ")
弹出框处理
- 网页中常用的弹出框
- alert 警告框
- confirm 确认框
- prompt 提示框
- 弹出框的处理方法
- 获取弹出框对象 alert=driver.switch_to.alert
- 调用
- alert.text 返回弹出的文字信息
- alert.accept() 接受对话框选项(同意)
- alert.dismiss() 取消对话框选项(取消)
滚动条操作
- 设置JavaScript脚本控制滚动条 js="window.scrollTo(0,1000)" 0表示左边距,1000表示上边距
- selenium调用执行JavaScript脚本的方法 driver.excute_script(js)
frame切换
- 什么是frame切换? 就是作用在当前页面中指定区域显示另一页面元素
- 具体的使用步骤
- driver.switch_to.frame(frame_reference) 切换到指定frame的方法 frame_reference:可以为frame框架的name、id或者定位到的frame元素
- driver.switch_to.default_content() 恢复默认页面方法
多窗口切换
- 出现过程 在HTML中,当点击链接或者按钮时,有的会在新的窗口打开页面
- 为什么需要切换? 页面存在多个窗口式,selenium默认焦点只会在主窗口上,不切换窗口,无法操作除主窗口以外的窗口内的元素
- 如何实现 在selenium中封装了获取当前窗口句柄,获取所有窗口句柄和切换到指定句柄窗口的方法
- driver.current_windwo_handle 获取当前窗口句柄
- driver.window_handles 获取所有窗口句柄
- driver.switch_to.window(handle) 切换到指定句柄窗口
from selenium import webdriver
driver = webdriver.Chrome()
# 获取当前句柄
current_handle = driver.current_window_handle
# 点击跳转之另一个页面
driver.find_elements_by_id("link").click()
# 获取所有句柄
handles = driver.window_handles
for handle in handles:
if handle != current_handle:
driver.switch_to.window(handle)
driver.find_elements_by_xpath("//span[contains(text(),'输入')]")
窗口截图
自动化脚本是由程序去执行的,因此有时候打印的错误信息并不是十分明确,如果在执行出错的时候对当前窗口截图保存,那么通过图片就可以非常直观的看到出错的原因.
在selenium中,提供了截图的方法,只需要调用即可.方法:driver.get_screenshot_as_file(imgpath) imgpath表示图片保存路径
验证码的处理方式
- 去掉验证码 测试环境下采用
- 设置万能验证码 生产环境和测试环境下采用
- 验证码识别技术 通过python-tesseract来识别图片类型验证码:识别率很难达到100%
- 记录cookie 通过记录cookie进行跳过登录
- Cookie是由web服务器生成的,并且保存在用户浏览器上的小文本文件,它可以包含用户相关的信息.
- Cookie数据格式:键值对组成(python中的字典)
- Cookie产生:客户端请求服务器,如果服务器需要记录该用户状态,就向客户端浏览器颁发一个Cookie数据
- Cookie使用:当浏览器再次请求该网站时,浏览器把请求的数据和Cookie数据一同提交给服务器,服务器检查该Cookie来辨认用户状态
- cookie的使用 selenium中对cookie操作提供相应的方法
- get_cookie(name) 获取指定cookie name为cookie的名称
- get_cookies() 获取本网站所有本地cookies
- add_cookie(cookie_dict) 添加cookie cookie_dict:一个字典对象,必选的键包括:"name"and"value"
- 步骤
- 打开百度url driver.get("https://www.baidu.com")
- 设置cookie信息 dirver.add_cookie({"name":"BDUSS","value":"根据实际情况编写值"})
UnitTest框架的使用
unitTest核心要素
- TestCase (测试用例)
- TestSuite (测试套件)
- TestRunner (以文本的形式运行测试用例)
- TestLoader (批量执行测试用例-搜索指定文件夹内指定字母开头的模式)[推荐]
- Fixture (固定装置 两个固定的函数,一个初始化时使用,一个结束时使用)
定义测试用例
- 导包 import unittest
- 定义测试类 新建测试类必须继承unittest.TestCase
- 定义测试方法:测试方法名称命名必须以test开头
测试套件TestSuite
- 实例化 suite=unittest.TestSuite()
- 添加用例 suite.addTest(ClassName("MethodName")) ClassName为类名,MethodName为方法名
- 添加扩展 suite.addTest(unittest.makeSuite(ClassName)) 搜索指定ClassName内test开头的方法并添加到测试套件中
- 执行测试套件中添加的用例(在2后使用)
- 实例化后去执行套件对象 runner=unittest.TextTestRunner()
- 调用run方法去执行 runner.run(suite)
TestLoader的用法
- 导包 import unittest
- 创建套件(一个目录下的所有文件代表一个套件) suite=unittest.TestLoader().discover("../cases",pattern="test*.py") 不写pattern属性,表示一个目录下的所有py文件
- 创建套件(用法跟TestLoader类似) suite=unittest.defaultTestLoader.discover("../cases",pattern="test*.py")
- 执行套件 unittest.TextTestRunner().run(suite)
Fixture的使用
其实就是两个函数,这个函数可以一起使用,也可以单独使用
函数级别
- 初始化函数 def setup()
- 结束函数 def tearDown()
类级别
- 开始函数 def setupClass()
- 结束函数 def teardownClass()
模块级别(了解)
- 开始函数 def setupModule()
- 结束函数 def teardownModule()
元素定位的另一种编写方式
- 导入By类 from selenium.webdriver.common.by import By
- 使用By类的方法 driver.find_element(By.ID,"#user").send_keys("name")
unittest的常用断言
在用户编写的每个测试方法中都必须有对结果正确与否的判断,否则就不是一个合格的单元测试方法,而unittest为用户提供了足够多的断言方法,需要抛出异常添加raise
import unittest
class TestToBeTest(unittest.TestCase):
def test_assertEqual(self):
try:
a, b = 100, 200
sum = 300
self.assertEqual(a + b, sum, "断言失败") # 如果a+b==sum,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_assertNotEqual(self):
try:
a, b = 100, 200
sum = 300
self.assertNotEqual(a + b, sum, "断言失败") # 如果a+b=!sum,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_assertTrue(self):
try:
a, b = 100, 200
sum = 300
self.assertTrue(a + b == sum, "表达式不对") # 如果是true,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_assertFalse(self):
try:
a, b = 100, 200
sum = 300
self.assertFalse(a + b == sum, "表达式错误") # 如果是false,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_assertIS(self):
try:
a, b = 100, 200
self.assertIs(a, b, "表达式错误") # 如果a是b,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_assertNotIs(self):
try:
a, b = 100, 100
self.assertIsNot(a, b, "表达式错误") # 如果a不是b,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_assertLn(self):
try:
a, b = "100", "100"
self.assertIn(a, b, "表达式错误") # 如果a在b中,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_asserNotLn(self):
try:
a, b = "100", "100"
self.assertNotIn(a, b, "表达式错误") # 如果a不在b中,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_asserIsInstance(self):
try:
a, b = object, object
self.assertIsInstance(a, b, "表达式错误") # 如果a是b的类型,就执行通过,否则断言失败
except AssertionError as e:
print(e)
def test_asserNotIsInstance(self):
try:
a, b = str, object
self.assertNotIsInstance(a, b, "表达式错误") # 如果a不是b的类型,就执行通过,否则断言失败
except AssertionError as e:
print(e)
if __name__ == '__main__':
unittest.main(verbosity=2)
参数化应用
- 什么是参数化? 通过参数的方式来传递数据,从而实现数据和脚本分立,并且可以实现用例的重复执行.unittest测试框架,本身不支持参数化,但是可以通过安装unittest扩展插件parameterized来实现
- 安装
- pip install parameterized
- pyCharm安装方式 File→setting→project
- 插件的应用
- 导包 from parameterized import parameterized
- 修饰测试函数 @parameterized.expend([数据])
- 数据格式 单个参数类型为列表,多个参数类型为列表嵌套元祖
- 在测试函数中的参数设置变量引用参数值,注意:变量的数量必须和数据值得个数相同
def get_data():
return [(1,2,3),(1,2,3),(1,2,3)]
class Test01(unittest.TestCase):
@parameterized.expend(get_data())
def test_add(self,a,b,result):
sum=add(a,b)
assert==result
print(result)
unittest的跳过
- 解释 对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行.
- 使用方式
- 直接将测试函数标记成跳过 @unittest.skip('代码未完成')
- 根据条件判断测试函数是否跳过 @unittest.skipIf(condition,reason)
with与with open区别
- 共同点 打开文件
- 区别 with open是执行打开与关闭的操作一起
with open("../report/text.txt","w",encoding="utf-8") as f:
f.read()
PO模式→V3
po页面层
# 用来封装对象----用户名、密码、登录等
from selenium import webdriver
class PageLogin:
def __init__(self):
self.driver = webdriver.Firefox()
self.driver.maximize_window()
self.driver.implicitly_wait(5)
self.driver.get("https://www.baidu.com")
# 点击登录 连接
def page_click_login_link(self):
pass
# 输入用户名
def page_input_username(self,username):
pass
# 输入密码
def page_input_pwd(self,password):
pass
# 输入验证码
def page_input_code(self,code):
pass
# 点击登录信息
def page_click_login_button(self):
pass
# 获取异常提示信息
def page_get_text(self):
pass
# 点击提示框确定按钮
def page_click_err_btn_ok(self):
pass
def page_login(self,username,password,code):
self.page_click_login_link()
self.page_input_username(username)
self.page_input_pwd(password)
self.page_input_code(code)
self.page_click_login_button()
po业务层
from parameterized import parameterized
from page.page_login import PageLogin
import unittest
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.login = PageLogin()
def tearDown(self) -> None:
self.login.driver.quit()
@parameterized.expand([("17327767735", "123456", "8888", "账号不存在")])
def test_login(self, username, password, code, expect):
self.login.page_login(username, password, code)
msg = self.login.page_get_text()
try:
self.assertEqual(msg, expect)
self.login.page_click_err_btn_ok()
except AssertionError:
# 存放截图代码
pass
po模式→v4
- base(基类) page页面一些公共的方法
- 初始化方法
- 查找元素方法
- 点击元素方法
- 输入方法
- 获取文本方法
- 截图方法
- page(页面对象) 一个页面封装成一个对象,继承base类
- 一个页面封装一个对象
- 将每个元素操作单独封装一个操作方法
- 组装,根据需求组装封装的操作方法
- scripts(业务层) 导包调用page页面
- 在unittest框架中不能使用def__init__()初始化方法
- 初始化方法 setup()
- 结束方法 teardown()
- 测试方法 根据要操作的业务来实现
扩展 loc变量:类型为元祖 *loc为解包
base文件夹下 base.py
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
class Base:
def __init__(self):
# 临时代替driver
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("http://192.168.1.105:8080/KBMS/")
# 查找元素方法
def base_find_element(self, loc, timeout=30, poll_frequency=0.5):
return WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(lambda x: x.find_element(*loc))
# 点击方法
def base_click(self, loc):
self.base_find_element(loc).click()
# 输入方法
def base_input(self, loc, value):
el = self.base_find_element(loc)
el.clear()
el.send_keys(value)
# 获取文本方法
def base_get_text(self, loc):
return self.base_find_element(loc).text
# 获取截图
def base_get_image(self):
self.driver.get_screenshot_as_file("../image/{}.png" .format(time.strftime("%Y_%m_%d %H_$M_%S ")))page文件夹下 __init__.py(多用于放元素属性) page_login.py
from selenium.webdriver.common.by import By
# # 登录链接
# login_link = By.PARTIAL_LINK_TEXT, "登录"
# 用户名
login_username = By.ID, "username"
# 密码
login_password = By.ID, "password"
# 登录按钮
login_btn = By.ID, "Layer1"
# 获取异常文本信息
login_err_info = By.CSS_SELECTOR, "layui-layer"from base.base import Base
import page
class PageLogin(Base):
# # 点击登录链接
# def page_click_login_link(self):
# self.base_click(page.login_link)
# 输入用户名
def page_input_username(self, username):
self.base_input(page.login_username, username)
# 输入用户密码
def page_input_password(self, password):
self.base_input(page.login_password, password)
# 点击登录
def page_click_login_btn(self):
self.base_click(page.login_btn)
# 组合业务方法
def page_login(self, username, password):
# self.page_click_login_link()
self.page_input_username(username)
self.page_input_password(password)
self.page_click_login_btn()scripts文件夹下 test_login.py
import unittest
from page.page_login import PageLogin
from parameterized import parameterized
import time
def get_data():
return [("admin", "admin")]
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.pageLogin = PageLogin()
def tearDown(self) -> None:
self.pageLogin.driver.quit()
@parameterized.expand(get_data())
def test_login(self, username, password):
self.pageLogin.page_login(username, password)
time.sleep(3)
if __name__ == '__main__':
unittest.main()
数据驱动
- 什么是数据驱动? 以数据来驱动整个测试用例的执行,也就是测试数据决定测试结果
- 数据驱动的特点?
- 可以把数据驱动理解为一种模式或者一种思想
- 数据驱动技术可以将用户把关注点放在对测试数据的构建和维护上,而不是直接维护脚本,可以利用同样的过程对不同的数据输入进行测试。
- 数据驱动的实线要依赖参数化的技术
- 传入数据的方式
- 直接定义在测试脚本中(简单直观,但代码和数据未实现真正的分离,不方便后期维护)
- 从文件读取数据,如JSON、excel、xml、txt等格式文件
- 从数据中读取数据
- 直接调用接口获取数据源
- 本地封装一些生成数据的方法
JSON操作
- json的特点
- json是纯文本
- json具有良好的自我描述性,便于阅读和编写
- json具有清晰的层级结构
- 有效的提升网络传输效率
- json的语法规则
- 大括号保存对象
- 中括号保存数组
- 对象数组可以相互嵌套
- 数据采用键值对表示
- 多个数据由逗号分隔
- json的值
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true或false)
- 数组(在中括号中)
- 对象(在大括号中)
- null
python字典与Json之间的转换
python字典转换为JSON字符串
import json
date = {
'id': 1,
'name': 'wupeng',
'school': None
}
json_str = json.dumps(date)
print(json_str)JSON字符串转换为python字典
import json
json_str = '{"id": 1,"name": "wupeng","school":null}'
python_str = json.loads(json_str)
print(python_str)
JSON的写入与读取
写入json文件
import json
data = {'name': 'wupeng', 'age': 18, 'school': None}
with open("./data/test.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False) # 第三个参数ascii码,改False处理中文读取json文件
import json
with open("./data/test.json", "r", encoding="utf-8") as f:
text = json.load(f)
print(text)
po模式→v5
- base
- - base.py(封装元素)
- - get_driver.py(封装dirver)
- data
- - data.json(存放参数化数据,json文件)
- - data.txt(存放参数化数据,txt文件)
- page
- - __init__.py(存放css、id等八大定位值)
- - page.clac.py(存放业务,以及组装业务)
- scripts
- - test.clac.py(存放测试脚本)
- tools
- - read_json.py(读取json文件,并获取值)
- - read_txt.py(读取txt文件,并获取值)
base.py
from selenium.webdriver.support.wait import WebDriverWait
import time
class Base:
def __init__(self, driver):
self.driver = driver
# 查找元素方法
def base_find_element(self, loc, timeout=30, poll_frequency=0.5):
return WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
lambda x: x.find_element(*loc))
# 点击方法
def base_click(self, loc):
self.base_find_element(loc).click()
# 输入方法
def base_input(self, loc, value):
el = self.base_find_element(loc)
el.clear()
el.send_keys(value)
# 获取文本方法
def base_get_text(self, loc):
return self.base_find_element(loc).text
# 获取value的属性方法
def base_get_value(self, loc):
# 使用get_attribute获取指定的元素值
self.base_find_element(loc).get_attribute("value")
# 获取截图
def base_get_image(self):
self.driver.get_screenshot_as_file("../image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")))get_driver.py
from selenium import webdriver
import page
class GetDriver:
driver = None
@classmethod
def get_driver(cls):
if cls.driver is None:
cls.driver = webdriver.Firefox()
cls.driver.maximize_window()
cls.driver.get(page.url)
return cls.driver
@classmethod
def quit_driver(cls):
if cls.driver:
cls.driver.quit()
# 一个坑,需要做dirver归Num,实现单例
cls.driver = Nonedata.json
{
"data1": {
"a": 1,
"b": 2
},
"data2": {
"a": 1234,
"b": 1232
},
"data3": {
"a": 1234,
"b": 1232
}
}data.txt
1,2
3,4
5,6__init__.py
from selenium.webdriver.common.by import By
url = "http://cal.apple886.com/"
clac_num = By.CSS_SELECTOR, "#simple9"
# 加号
clac_add = By.CSS_SELECTOR, "#simpleAdd"
# 等号
clac_eq = By.CSS_SELECTOR, "#simpleEqual"
# 结果
clac_result = By.CSS_SELECTOR, "#resultIpt"
# 清屏
clac_clear = By.CSS_SELECTOR, "#simpleClearAllBtn"page_clac.py
from selenium.webdriver.common.by import By
import page
from base.base import Base
class PageClac(Base):
# 点击数字
num = 12
def page_click_num(self, num):
for n in str(num):
loc = By.CSS_SELECTOR, "#simple{}".format(n)
self.base_click(loc)
# self.base_click(page.clac_num)
# 点击加号
def page_click_add(self):
self.base_click(page.clac_add)
# 点击等号
def page_click_eq(self):
self.base_click(page.clac_eq)
# 获取结果方法
def page_click_result(self):
self.base_click(page.clac_result)
# 点击清屏
def page_click_clear(self):
self.base_click(page.clac_clear)
# 组装业务方法
def page_add_clac(self, a, b):
self.page_click_num(a)
self.page_click_add()
self.page_click_num(b)
self.page_click_eq()test_clac.py
import unittest
from page.page_clac import PageClac
from parameterized import parameterized
from tools.read_txt import read_txt
from base.get_driver import GetDriver
class TestClac(unittest.TestCase):
driver = None
@classmethod
def setUpClass(cls) -> None:
cls.driver = GetDriver().get_driver()
cls.clac = PageClac(cls.driver)
@classmethod
def tearDownClass(cls) -> None:
GetDriver().quit_driver()
@parameterized.expand(read_txt("data.txt"))
def test_add_clac(self, a, b):
self.clac.page_add_clac(a, b)read_json.py
import json
def read_json(fileName):
filepath = "../data/" + fileName
arr = []
with open(filepath, "r", encoding="utf-8") as file:
datas = json.load(file)
for data in datas.values():
arr.append((data['a'], data['b']))
return arrread_txt.py
def read_txt(fileName):
filepath = "../data/" + fileName
arr = []
with open(filepath, "r", encoding="utf-8") as file:
datas = file.readlines()
for data in datas:
arr.append(tuple(data.strip().split(",")))
return arr
日志特点及级别
元素的常用操作方法
# 清除文本
driver.find_element_by_id("#id").clear()
# 文本写入
driver.find_element_by_id("#id").send_keys()
# 点击
driver.find_element_by_id("#id").click()
# 获取class元素的属性
driver.find_element_by_id("#id").get_attribute("class")
# 获取输入框里面的文字
driver.find_element_by_id("#id").get_attribute("value")
浏览器的常用方法
# 模拟浏览器最大化
driver.maximize_window()
# 设置指定的浏览器的大小
driver.set_window_size(100,200)
# 模拟浏览器的位置
driver.set_window_position(100,200)
# 模拟浏览器的后退
driver.back()
# 模拟浏览器的前进
driver.forward()
# 模拟浏览器关闭按钮(关闭单个窗口)
driver.refresh()
# 关闭所有webdriver启动的窗口
driver.close()
driver.quit()
webdriver的其他方法
# 获取用户名文本框大小
driver.find_element_by_id("#id").size
# 获取文本值
driver.find_element_by_id("#id").text
# 获取元素属性值
driver.find_element_by_id("#id").get_attribute("href")
# 获取当前页面title
driver.title
# 获取当前页面url
driver.current_url
# 判断span元素是否显示
driver.find_element_by_id("#id").is_displayed()
# 判断取消按钮是否可用
driver.find_element_by_id("#id").is_enabled()
webdriver操作鼠标
from selenium.webdriver.common.action_chains import ActionChains
# 使用键盘操作需要导入
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("https://www.baidu.com/")
# 指定元素鼠标右键
ActionChains(driver).context_click(driver.find_element_by_id("su")).perform()
# 指定元素鼠标双击
ActionChains(driver).double_click(driver.find_element_by_id("su")).perform()
# 拖动
box1=driver.find_element_by_id("box1")
box2=driver.find_element_by_id("box2")
# 将box1拖动到box2的位置
ActionChains(driver).drag_and_drop(box1,box2).perform()
# 将box1拖动到坐标500,0的位置
ActionChains(driver).drag_and_drop_by_offset(box1,500,0).perform()
# 鼠标悬停(比较经典的就是悬浮框)
ActionChains(driver).move_to_element(driver.find_element_by_id("su")).perform()
# 删除键
driver.find_element_by_id("su").send_keys(Keys.BACK_SPACE)
# 空格键
driver.find_element_by_id("su").send_keys(Keys.SPACE)
# tab键
driver.find_element_by_id("su").send_keys(Keys.TAB)
# 回退键
driver.find_element_by_id("su").send_keys(Keys.ESCAPE)
# 回车键
driver.find_element_by_id("su").send_keys(Keys.ENTER)
# 全选
driver.find_element_by_id("su").send_keys(Keys.CONTROL,"c")
# 复制
driver.find_element_by_id("su").send_keys(Keys.CONTROL,"v")
元素等待(主要隐式等待)
调用方法:
driver.implicitly_wait(10)
说明:如果定位某一元素定位失败,那么就会触发隐式等待有效时长,如果在指定时长内加载完毕,则继续执行,否则跑出NoSuchElementException异常,如果元素在第一次就定位到则不会触发隐式等待时长.
文件上传
待续----
下拉框
select的相关方法:
- select_by_index() 根据option索引来定位,从0开始
- select_by_value() 根据option属性value值来定位
- select_by_visible_text() 根据option显示文本来定位
代码演示:(需要导入select的包)
from selenium import webdriver
from selenium.webdriver.support.select import Select
import time
driver=webdriver.Chrome()
driver.get("https://yqmbqldc.mazhoudao.com/investigation/index01.html")
driver.find_element_by_xpath('/html/body/div/div[2]/label').click()
time.sleep(3)
driver.find_element_by_xpath('/html/body/div/a').click()
time.sleep(3)
driver.find_element_by_xpath('/html/body/div/div/div[1]/a').click()
time.sleep(3)
select=driver.find_element_by_id("whcd")
Select(select).select_by_index(1)
time.sleep(3)
frame切换
使用WebDriver对象swith_to属性
wd.switch_to.frame("属性值") 属性值可以是id的属性,也可以是name的属性
如果没有id跟name,可以根据frame的元素位置或者属性特征,使用find系列的方法
wd.switch_to.frame(wd.find_element_by_tag_name("iframe"))
警告框
HTML中常用的对话框有三种,处理的方法都大同小异
- alert
- confirm
- prompt
处理方法:
- text 返回alert等文字信息
- accept 接受对话框选项
- dismiss 取消对话框选项
示例代码:
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("F:\\Users\\R\\untitled2\\thname.html")
driver.find_element_by_id("btn").click()
time.sleep(2)
alert=driver._switch_to.alert.text
print(alert)
滚动条操作
为什么需要滚动条的操作
- webDriver类库中并没有直接提供对滚动条进行操作方法啊,但是它提供了可调用JavaScript脚本的方法,所以我们可以通过JavaScript脚本来达到操作滚动条的目的
- 滚动条一种可控制程序显示范围的组件.
示例代码:
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("F:\\Users\\R\\untitled2\\thname.html")
js1="window.scrollTo(0,0)"
js2="window.scrollTo(0,10000)"
time.sleep(3)
# 执行最底层
driver.execute_script(js2)
# 执行最顶层
driver.execute_script(js1)
切换表单方法
# 切换到frame子页面 browser.switch_to.frame('iframeResult') time.sleep(3) browser.find_element_by_xpath('/html/body/a').click() #browser.find_element_by_link_text('这是一个链接使用了 href 属性').click() time.sleep(3) # 切回父页面(必须执行) browser.switch_to.default_content() #browser.find_element_by_css_selector('html body a').click() #time.sleep(3) browser.quit()
多窗口切换
在webDriver中封装了获取当前窗口句柄方法和获取所有窗口句柄的方法以及切换指定句柄窗口的方法
方法:
- driver.current_window_handle 获取当前窗口句柄
- driver.window_handles 获取所有窗口句柄
- driver.switch_to.window(handle) 切换指定句柄窗口
示例代码:
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get("https://www.baidu.com")
# 获取当前句柄
handle=driver.current_window_handle
driver.find_element_by_id("jgwab").click()
time.sleep(3)
handles=driver.window_handles
for hand in handles:
if hand!=handle:
driver.switch_to.window(hand)
driver.find_element_by_xpath('/html/body/div[1]/div[1]/div/div/div/input').send_keys("犯罪")
time.sleep(3)
# 使用如下的网页返回,可以退到原来的网页
driver.switch_to.window(handle)
窗口截图
说明:在webDriver类库中,提供了截图方法,我们只需要调用即可
方法:get_screenshot_as_file(imgpath) 截取当前窗口
代码实例(一般出现在提交或者容易报错的地方进行截图)
driver.get_screenshot_as_file("./imgages.png")
验证码的处理
在web应用中,大部分系统在用户登录的时候都要求输入雅正吗,而我们在设计自动化脚本的时候,就需要面临这验证码的问题
所以出现了如下的解决方式:
- 去掉验证码(测试环境下--采用)
- 设置万能验证码(生产环境下--采用)
- 验证码识别技术(通过Python-tesseract来识别图片类型验证码,识别率很难达到100%)
- 记录cookie(通过记录cookie进行登录--推荐)
cookie
cookie是什么?
- cookie是一小段的文本信息,格式是python中的字段(键值对组成)
- cookie产生:客户端请求服务器,如果服务器需要记录该用户状态,就像客户端浏览器办法一个cookie格式
- cookie使用:当浏览器在请求该网站时,浏览器吧请求的网址连同该cookie一同提交给服务器,服务器检查该cookie,以此来辨认用户状态.
为什么要记录cookie?
- 用户第一次登陆时,勾选下次直接登录或者记住密码,就是采用记录cookie实现的
- cookie内记录用户名和密码(加密)信息,之哟啊请求时服务器收到cookie,就识别成功,默认已登录
记录cookie,方法如下:
- get_cookie(name),name为键名 获取指定cookie
- get_cookies() 获取本网站所有本地cookies
- add_cookies(str),str为python中的字典格式 添加cookie
代码实例(需要在火狐中寻找cookie中BAIDUID与BDUSS的键值对值)
from selenium import webdriver
import time
driver=webdriver.Firefox()
driver.get("https://www.baidu.com")
driver.add_cookie({"name":"BAIDUID","value":"813F26D2B40131AA64B1C59546F4FA88:FG=1"})
driver.add_cookie({"name":"BDUSS","value":"N2cnRuQXEydGxIbnVIWVotSk11WkVpcjI1VnAxeU5WeFVrcktuNEhncGFUWHhlSVFBQUFBJCQAAAAAAAAAAAEAAABChzrXxOPOtNT4zfy8xwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFrAVF5awFReWV"})
time.sleep(3)
driver.refresh()
time.sleep(2)
UnitTest框架
好处
- 能够组织多个用例去执行
- 提供丰富的断言方法
- 提供丰富的日志与测试结果
核心要素
- TestCase
- TestSuite
- TextTestRunner
- Fixture
TestCase的使用(测试方法名称命名必须以test开头,原因是unittest.TestCase批量运行的方法是搜索执行test开头的方法)
- 导包:import unittest 导入unittest框架
- 继承:unittest.TestCase 新建测试类继承unittest.TestCase
UnitTest断言
断言方式(主要的两种)
- assertEqual(arg1,arg2,msg=None) 验证arg=arg2,不等则fail(掌握)
- assertIn(arg1,arg2,msg=None) 验证arg1是arg2的子串,不是则fail
改良版全功能自动化用例
# 登录模块 from selenium import webdriver import unittest import time import csv class Login(unittest.TestCase): def setUp(self) -> None: self.browser=webdriver.Chrome() self.browser.get("http://192.168.159.1:8082/") time.sleep(3) def tearDown(self) -> None: self.browser.quit() # 定义登录带参函数 def login(self,username,password): self.browser.find_element_by_xpath("//*[@id='shop_header']/div[2]/ul/li[7]/a").click() time.sleep(3) self.browser.find_element_by_id("user_name").send_keys(username) self.browser.find_element_by_id("user_password").send_keys(password) self.browser.find_element_by_xpath("//*[@id='login_form']/div[4]/div/button[1]").click() time.sleep(3) # 定义退出函数 def loginOut(self): self.browser.find_element_by_link_text("退出").click() # 登录成功测试 def testLoginSuccess(self): self.login("wupeng","123456") # 用户名为1测试 def testUserNull(self): self.login("","123456") # self.assertTrue(self.browser.find_element_by_xpath("//*[@id='login_form']/div[2]/div/label").text=="请输入会员登录名称!") info=self.browser.find_element_by_xpath("//*[@id='login_form']/div[2]/div/label").text self.assertIn("请输入会员登录名称",info) if __name__ == '__main__': unittest.main(verbosity=2) # 搜索模块 from login import dbSlogin import unittest import time class Search(unittest.TestCase): myself=dbSlogin.Login myself.setUp(myself) myself.login(myself,"wupeng","123456") # 输入正确搜索字段搜索 def testSearch(self): self.myself.browser.find_element_by_xpath("//*[@id='shop_top_search']/form/div/input").send_keys("苹果") self.myself.browser.find_element_by_xpath("//*[@id='shop_top_search']/form/div/button").click() time.sleep(3) self.myself.tearDown(self.myself) if __name__ == '__main__': unittest.main(verbosity=2)
套件测试多条用例(最终所有用例写完执行套件即可)
示例代码:
import unittest
import time
from login import HTMLTestReport
import os
#生成报告的时间
current_time = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
# 用例路径 默认获取当前目录
case_path = os.getcwd()
# 报告存放路径 默认获取当前目录
report_path = os.path.join(os.getcwd(), 'DBShop_'+current_time+".html")
def all_case():
# test*.py 用例的文件必须是test开头 并且只能包含字母或者下划线,否则有可能识别不了,无法全部执行
discover = unittest.defaultTestLoader.discover(case_path, pattern="ts*.py", top_level_dir=None)
print(discover)
return discover
if __name__ == "__main__":
fp = open(report_path, "wb")
# 用例标题,执行者可以自己更改
runner = HTMLTestReport.HTMLTestRunner(stream=fp, title="自动化测试报告", description='自动化测试报告', tester='wupeng')
runner.run(all_case())
fp.close()
C:\其他