本文主要是介绍测试技能整理HM-UI自动化测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
自动化测试理论
UI:User Interface(⽤户接⼝-⽤户界⾯),主要包括:app、web
UI自动化测试:使用工具或代码执行用例的过程
什么样的项⽬适合做自动化:
1、需要回归测试项⽬(甲方自营项目、金融、电商)
2、需求变动不频繁:稳定的模块
3、项目周期⻓的项⽬:(甲⽅⾃营项⽬、6个⽉以上的外包)
自动化测试工具及环境
工具说明
QTP:商业、收费、⽀持UI
robot Framework:python扩展库、使⽤封装好的关键字驱动、半代码⽔平、⽀持UI
selenium:开源、免费、主流⽀持UI
selenium介绍
提示:
1、selenium-grid可以做分布式(批量在不同平台中运⾏⽤例),⾃动化⽤例较多时、或测试不
同浏览器在不同平台运⾏时可以使⽤。
2、对⻚⾯元素实施⾃动化测试,主要使⽤:webdriver
环境搭建
所需环境: pthon解释器+pycharm+selenium+浏览器+浏览器驱动
selenium
pip install selenium
浏览器驱动
chrome: http://npm.taobao.org/mirrors/chromedriver/
提示:浏览器驱动⼤版本必须和浏览器版本⼀致
使用:
windows:
1、解压下载的驱动,获取到chromedriver.exe
2、将chromedriver.exe复制到python.exe所在⽬录即可(避免再次将chromedrver.exe添加path变量)
元素定位
什么是元素定位?
通过代码调⽤⽅法查找元素
元素定位⽅法
1、id
2、name
3、class
4、tag_name
5、link_text
6、partial_link_text
7、xpath
8、css
步骤
1、打开⾕歌浏览器
2、输⼊url
3、找元素及操作
4、关闭浏览器
from time import sleep
from selenium import webdriver
# 1、打开⾕歌浏览器(实例化浏览器对象)
driver = webdriver.Chrome()
# 2、输⼊url
driver.get("http://tpshop-test.itheima.net/Home/user/login.html")
# 3、找元素及操作
# 4、关闭浏览器
sleep(3)
driver.quit()
id定位
⽅法: driver.find_element_by_id(“id值”)
前置: 标签必须有id属性
输⼊⽅法: 元素.send_keys(“内容”)
from time import sleep
from selenium import webdriver
# 1、获取浏览器
driver = webdriver.Chrome()
# 2、打开url
driver.get("file:///Users/lgy/Documents/fodder/web/%E6%B3%A8%E5%86%8CA.html
")
# 3、查找操作元素
# ⽤户名 -> id ->driver.find_element_by_id("id")
# 元素.send_keys()输⼊⽅法
driver.find_element_by_id("userA").send_keys("admin")
# 密码
driver.find_element_by_id("passwordA").send_keys("123456")
# 4、关闭浏览器
sleep(3)
driver.quit()
name定位
⽅法: driver.find_element_by_name(“name属性值”)
前置: 标签必须name属性
特点: 当前⻚⾯可以重复
提示: 由于name属性值可以重复,所以使⽤时需要查看是否为唯⼀。
class定位
⽅法: driver.find_element_by_class_name(“class属性值”)
前置: 标签必须有class属性
特点: class属性值可以有多个值
说明:如果标签有多个class值,使⽤任何⼀个都可以。如:c1
tag_name定位
说明:根据的标签名进⾏定位
⽅法: driver.find_elemet_by_tag_name(“标签名”)
提示: 如果⻚⾯存在多个相同标签,默认返回第⼀个。
link_text定位
说明: 根据链接⽂本(a标签)定位
⽅法: driver.find_element_by_link_text(“链接⽂本”)
特点: 传⼊的链接⽂本,必须全部匹配,不能模糊
partial_link_text定位
说明: 根据链接⽂本(a标签)定位
⽅法: driver.find_element_by_partial_link_text(“链接⽂本”)
特点: 传⼊的链接⽂本,⽀持模糊匹配(传⼊局部⽂字)
扩展-查找⼀组元素
说明:返回列表格式,要使⽤需要添加下标或遍历。
⽅法: driver.find_elements_by_xxxx(),
提示: ⼋⼤元素定位⽅法,都可以使⽤⼀组元素定位,如果没有搜索到符合标签,返回空
列表。
xpath
说明:xpath是xml path简称,使⽤标签路径来定位。
绝对路径:从根⽬录开始,逐级查找标签。
相对路径:从任意层级开始,查找标签
策略(⽅法):
1、路径
2、属性
3、属性与逻辑(多个属性)
4、属性与层级(路径)
⽅法: driver.find_element_by_xpaht(“表达式”)
# 绝对路径
el =
driver.find_element_by_xpath("/html/body/form/div/fieldset/center/p[1]/input
")
el.send_keys("admin")
sleep(2)
# 清除内容
el.clear()
# 相对路径
driver.find_element_by_xpath("//p[1]/input").send_keys("123")
属性
单属性: // * [@属性名=‘属性值’]
多属性: // * [@属性名=‘属性值’ and @属性名=‘属性值’]
提示:可以使⽤任何属性。
层级与属性
说明: 如果元素现有的属性不能唯⼀匹配,需要结合层级使⽤
语法:
//⽗标签/⼦标签 必须为直属⼦级
//⽗标签[@属性=‘值’]//后代标签 ⽗和后代之间可以跨越元素
扩展
根据显示⽂本定位: //*[text()=‘⽂本值’]
属性值模糊匹配: // * [contains(@属性名,‘属性部分值’)]
from time import sleep
from selenium import webdriver
# 1、获取浏览器
driver = webdriver.Chrome()
# 2、打开url
driver.get("http://hmshop-test.itheima.net/")
# 3、查找操作元素
# 点击登录链接 ⽂本
driver.find_element_by_xpath("//*[text()='登录']").click()
# 输⼊⽤户名 属性
driver.find_element_by_xpath("//*[@placeholder='⼿机号/邮
箱']").send_keys("13600001111")
# 密码 包含
driver.find_element_by_xpath("//*
[contains(@placeholder,'密')]").send_keys("123456")
# 验证码 多属性
driver.find_element_by_xpath("//*[@placeholder='验证码' and
@name='verify_code']").send_keys("8888")
# 登录按钮 层级
driver.find_element_by_xpath("//*[@class='login_bnt']/a").click()
# 4、关闭浏览器
sleep(3)
driver.quit()
css选择器
说明:css选择器是html查找元素的⼯具
策略:
1、id选择器
2、类选择器
3、标签选择器
4、属性选择器
5、层级选择器
id选择器
语法: #id属性值
前置: 标签必须id属性
类选择器
语法: .class属性值
前置: 标签必须class属性
标签选择器
语法: 标签名
提示: 注意标签是否在⻚⾯中唯⼀,否则返回单个或所有
属性选择器
语法: [属性名=‘属性值’]
说明: 标签任意属性都可以
案例
# ⽤户名 id选择->#id属性值
driver.find_element_by_css_selector("#userA").send_keys("admin")
# 密码 属性选择器->[属性名='属性值']
driver.find_element_by_css_selector("[name='passwordA']").send_keys("123456")
# 电话 类选择器->.class属性值
driver.find_element_by_css_selector(".telA").send_keys("18600000000")
# 确定 标签选择器-标签名
sleep(2)
driver.find_element_by_css_selector("button").click()
层级选择器
⽗⼦关系: 选择器>选择器 如: #p1>input
后代关系: 选择器 选择器 如: #p1 input
提示: 选择器使⽤任何⼀种css选择器(id选择器、类选择器、属性选择器、标签选
择器)都可以
find_element
说明: ⼋种元素定位⽅法底层使⽤的查找元素⽅法都是find_element,通过By不同的值区
分定位⽅式
学习此⽅法⽬的: 后期为了查找元素⽅法的封装
"""⽬标:讲解find_element使⽤场景:后期项⽬封装中,使⽤元素查找⽅法⽬的:对后期封装元素查找⽅法
"""
driver.find_element(By.ID,"userA").send_keys("admin")
driver.find_element(By.NAME,"passwordA").send_keys("123456")
driver.find_element(By.CLASS_NAME,"telA").send_keys("18600000000")
sleep(2)
driver.find_element(By.TAG_NAME,"button").click()
结论:
1、⾸推css定位,原因执⾏速度快。
①如果有ID属性,使⽤#id
②没有id属性,使⽤其他有的属性(能代表唯⼀的属性)
③如果属性都带不了唯⼀,使⽤层级
2、如果css解决不了,使⽤xpath。
元素操作
1、操作⽅法
2、获取⽅法
常用操作方法
元素=driver.find_element()
点击:元素.click()
输⼊:元素.send_keys(内容)
清空:元素.clear()
获取元素信息
⽅法
获取⼤⼩: 元素.size
获取⽂本: 元素.text
获取属性: 元素.get_attribute(‘属性名’)
元素是否可⻅: 元素.is_displayed()
元素是否可⽤: 元素.is_enabled()
元素是否选中: 元素.is_selected()
# 获取⼤⼩ 元素.size
user = driver.find_element(By.CSS_SELECTOR,"#userA").size
print("⽤户名输⼊框的⼤⼩:",user)
# 获取内容 元素.text
a_text = driver.find_element(By.TAG_NAME,"a").text
print("第⼀个a标签的⽂本:",a_text)
# 获取属性 超连接地址
a_href = driver.find_element(By.TAG_NAME,"a").get_attribute("href")
print("第⼀个a标签的链接:",a_href)
# 判断span标签是否可⻅ 元素.is_displayed
span = driver.find_element(By.TAG_NAME,"span").is_displayed()
print("span是否可⻅:",span)
# 判断取消按钮是否可⽤ is_enabled
btn_is_enabled = driver.find_element(By.CSS_SELECTOR,"#cancelA").is_enabled()
print("取消按钮是否可⽤:",btn_is_enabled)
# 旅游是否选中 is_selected
is_selected = driver.find_element(By.CSS_SELECTOR,"#lyA").is_selected()
print("旅游是否被选中:",is_selected)
浏览器操作
浏览器常用api
# 最⼤化浏览器
driver.maximize_window()
sleep(3)
# 设置窗⼝⼤⼩
driver.set_window_size(500,700)
sleep(3)
driver.set_window_position(0,500)
sleep(3)
# 点击新浪
driver.find_element_by_partial_link_text("新浪").click()
sleep(3)
# 后退
driver.back()
sleep(3)
# 前进
driver.forward()
sleep(3)
driver.refresh()
浏览器常用获取信息api
重点:
1、close关闭当前焦点所在窗⼝
2、quit关闭的是浏览器
3、启动那个窗内⼝,默认焦点就在那个窗⼝。如果需要切换到别的窗⼝,需要调⽤api⽅法切
换。
# 1、获取浏览器
driver = webdriver.Chrome()
# 2、打开url
driver.get("file:///Users/lgy/Documents/fodder/web/Register.html")
# 最⼤化浏览器
driver.maximize_window()
# 获取当前窗⼝标题
print("当前窗⼝title:",driver.title)
# 获取当前窗⼝url
print("当前窗⼝url:",driver.current_url)
sleep(3)
driver.find_element_by_partial_link_text("注册A⽹⻚").click()
# 获取当前窗⼝标题
print("当前窗⼝title:",driver.title)
# 获取当前窗⼝url
print("当前窗⼝url:",driver.current_url)
sleep(3)
# 关闭当前窗内⼝
driver.close()
# 4、关闭浏览器
sleep(3)
driver.quit()
页面交互
下拉框
方式:
1、使⽤css或xpth (推荐)
2、使⽤专属Select类
方式1:
# 点击⼴州
driver.find_element(By.CSS_SELECTOR, "[value='gz']").click()
sleep(2)
driver.find_element(By.CSS_SELECTOR, "[value='sh']").click()
sleep(2)
driver.find_element(By.CSS_SELECTOR, "[value='bj']").click()
方式2:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
# 使⽤Select类来实现
# 1、定位下拉框元素 select
el = driver.find_element(By.CSS_SELECTOR,"#selectA")
# 2、实例化Select对象
select = Select(el)
# 3、使⽤下标定位⼴州
select.select_by_index(2)
sleep(2)
# 使⽤value定位上海
select.select_by_value("sh")
# 使⽤⽂本定位 北京
sleep(2)
select.select_by_visible_text("A北京")
弹窗
如果⻚⾯操作过程中,有弹窗出现,不处理,⽆法继续对⻚⾯操作
弹窗类型?
1、js原⽣弹窗(警告框、输⼊框、提示框)必须处理
2、开发使⽤标签⾃定义弹窗效果 (不⽤处理,正常操作即可。)
处理:
1、获取弹窗对象
2、点⽤同意或取消⽅法
# 点击弹窗
driver.find_element(By.ID,"alerta").click()
sleep(2)
# 获取弹窗对象
el = driver.switch_to.alert
# 处理弹窗 同意/取消
# el.dismiss() # 取消
# print("弹出⽂本:",el.text)
el.accept() # 同意
sleep(2)
# 输⼊⽤户名
driver.find_element(By.CSS_SELECTOR,"#userA").send_keys("admin")
滚动条
步骤:
1、定义js语句
2、调⽤执⾏js⽅法
# js -> 向下滑动10000像素
# js_down = "window.scrollTo(0,10000)"
# 动态执⾏滑倒底部 向下滑动滚动条⾼度
# js(0,10000) 第⼀个0为⽔平滚动条
js_down = "window.scrollTo(0,document.body.scrollHeight)"
# 执⾏js⽅法
driver.execute_script(js_down)
sleep(2)
# js—> 向上
js_top = "window.scrollTo(0,0)"
driver.execute_script(js_top)
鼠标操作
1、双击⽅法
2、右击⽅法
3、悬停
4、拖拽
from selenium.webdriver import ActionChains
# 获取ActionChains对象
action = ActionChains(driver)
# 练习 1
# 查找注册按钮
el = driver.find_element(By.CSS_SELECTOR,"button")
sleep(2)
# 调⽤悬停⽅法
action.move_to_element(el).perform()
# # 练习2
# username = driver.find_element(By.CSS_SELECTOR,"#userA")
# # 右击
# action.context_click(username).perform()
# 练习3
username = driver.find_element(By.CSS_SELECTOR,"#userA")
username.send_keys("admin")
sleep(3)
# 双击
action.double_click(username).perform()
# 拖拽
action = ActionChains(driver)
div1 = driver.find_element(By.CSS_SELECTOR, "#div1")
div2 = driver.find_element(By.CSS_SELECTOR, "#div2")
action.drag_and_drop(div1, div2).perform()
高级API
代码执⾏过程中,第⼀次未找到元素,先不抛出异常。激活等待时间,在等待过程中如果找到元素
就执⾏
为什么要等待?
由于⽹络或配置原因,导致元素未加载出来,⽽代码已执⾏,会触发异常。
元素等待类型
1、隐式等待
2、显示等待
3、强制等待 -->time.seep(秒)
隐式元素等待
说明: 针对全部元素⽣效
⽅法: driver.implicitly_wait(秒)
提示:在项⽬中,如果未封装⾃动化框架时,推荐使⽤
显示等待
说明: 针对单个元素⽣效,可以修改查找频率和超时时间。
特点: 查找并返回元素
from selenium.webdriver.support.wait import WebDriverWait
# 2、显示等待 -> 返回查找到的元素
el = WebDriverWait(driver,10,0.5).until(lambda x:
x.find_element(By.CSS_SELECTOR,"#userAA"))
el.send_keys("admin")
强制等待
语法: sleep(10)
提示: 执⾏到这句必须等待10秒,不灵活。
示例
from time import sleep
sleep(10)
操作frame框架的元素
frame(iframe)标签作⽤–是什么
在⻚⾯中加载另⼀个⻚⾯
为什么处理iframe(frane)?
焦点默认在启动⻚⾯,如果不出处理iframe,⽆法操作iframe嵌⼊的⻚⾯元素
如何处理?
1、切换到iframe driver.switch_to.frame(iframe元素)
2、操作元素
3、回到默认⻚⾯ driver.switch_to.default_content()
# 获取注册A iframe元素
A = driver.find_element(By.CSS_SELECTOR, "#idframe1")
# 1、切换到A
driver.switch_to.frame(A)
# 2、注册A操作
driver.find_element(By.CSS_SELECTOR,"#userA").send_keys("admin")
# 3、回到默认⽬录 注册时例.html
driver.switch_to.default_content()
# 4、获取注册B iframe元素
B = driver.find_element(By.CSS_SELECTOR, "#idframe2")
# 5、切换到B
driver.switch_to.frame(B)
# 6、注册B操作
driver.find_element(By.CSS_SELECTOR,"#userB").send_keys("admin")
切换多窗口
selenium默认启动时,所有的焦点在启动窗⼝,那么意味着⽆法操作其他窗⼝的标签
步骤:
1、获取窗⼝句柄 driver.window_handles
2、使⽤句柄切换窗⼝ driver.switch_to.widnow(handle)
句柄:窗⼝的唯⼀标识符。
"""需求:1、打开注册示例⻚⾯2、点击注册A⽹⻚链接3、填写注册A⽹⻚内容
"""
print("操作之前所有窗⼝的句柄:", driver.window_handles)
driver.find_element(By.LINK_TEXT, "注册A⽹⻚").click()
handles = driver.window_handles
print("操作之后所有窗⼝的句柄:", handles)
# 重点:切换窗⼝
driver.switch_to.window(handles[1])
# 填写注册A⽹⻚ ⽤户名
driver.find_element(By.CSS_SELECTOR, "#userA").send_keys("admin")
多窗⼝之间切换⼯具封装
def switch_window(title):# 1、获取所有窗⼝句柄handels = driver.window_handles# 2、遍历句柄进⾏切换for handel in handels:# 操作driver.switch_to.window(handel)# 获取当前窗⼝title 并且 判断是否⾃⼰需要的窗⼝if driver.title == title:# 操作代码return "已找到{}窗⼝,并且已切换成功".format(title)title_A = "注册A"
title_B = "注册B"
# 打开注册A和注册B⽹⻚
driver.find_element(By.LINK_TEXT, "注册A⽹⻚").click()
driver.find_element(By.LINK_TEXT, "注册B⽹⻚").click()
# 填写注册A⽹⻚ ⽤户名
switch_window(title_A)
driver.find_element(By.CSS_SELECTOR, "#userA").send_keys("admin")
switch_window(title_B)
driver.find_element(By.CSS_SELECTOR, "#userB").send_keys("admin")
截图
driver.get_screenshot_as_file(“xxx.png”)
扩展-图⽚命名添加时间戳
driver.get_screenshot_as_file(“error_{}.png”.format(time.strftime(“%Y_%m_%d
%H_%M_%S”)))
验证码处理
1、去除验证码
2、使⽤万能验证码
3、使⽤图⽚识别技术(识别效率低)
4、使⽤cookie
cookie
由服务器⽣成,存储在客户端的登录凭证
1、获取cookie # 获取所有driver.get_cookies()
2、添加cookie # driver.add_cookie(data)
data = {“name”:“BDUSS”,“value”:“由于安全问题,暂时删除。”}
PO模式
PO:Page Object(⻚⾯对象),将⾃动化涉及的⻚⾯或模块封装成对象
解决问题;
1、代码复⽤性
2、便于维护(脚本层与业务分离)-- 如果元素信息发⽣变化了,也不⽤去修改脚本
PO如何做
Base层:
存放所有⻚⾯公共⽅法
Page层:
基于⻚⾯或模块单独封装当前⻚⾯要操作的对象
Script层:
脚本层+unittest
⾮PO模式代码实现
class TestLogin(unittest.TestCase):
def setUp(self) -> None:self.driver = webdriver.Chrome()self.driver.maximize_window()self.driver.get("http://hmshop-test.itheima.net/Home/user/login.html")self.driver.implicitly_wait(10)def tearDown(self) -> None:sleep(5)self.driver.quit()def test01_login(self):# 输⼊⽤户名self.driver.find_element_by_css_selector("#username").send_keys("13600001111")# 输⼊密码self.driver.find_element_by_css_selector("#password").send_keys("123456")# 输⼊验证码self.driver.find_element_by_css_selector("#verify_code").send_keys("8888")# 点击登录self.driver.find_element_by_css_selector(".J-login-submit").click()
PO设计
结构
base: 存放所有Page页面公共方法
page: 将页面封装为对象
script: 测试脚本
案例(登录):
base: 分析要实现页面公共方法
"""
Base类:存放所有Page页面公共操作方法!
"""
from selenium.webdriver.support.wait import WebDriverWait
class Base:
def __init__(self, driver):
self.driver = driver
# 查找元素
# 输入方法
# 点击方法
# 获取文本值方法
base实现
class Base:
def __init__(self, driver):
self.driver = driver
# 查找元素
def base_find(self, loc, timeout=10, poll_frequency=0.5):
# 显示等待 -> 查找元素 loc = (By.ID,"userA")
return WebDriverWait(self.driver, timeout,
poll_frequency).until(lambda x: x.find_element(loc[0], loc[1]))
# 输入方法
def base_input(self, loc, value):
# 1、获取元素
el = self.base_find(loc)
# 2、清空操作
el.clear()
# 3、输入内容
el.send_keys(value)
# 点击方法
def base_click(self, loc):
self.base_find(loc).click()
# 获取文本值方法
def base_get_text(self, loc):
return self.base_find(loc).text
page结构搭建
"""
模块名:page_模块单词
类名:大驼峰将模块移植进来,去掉下划线和数字。
方法:自动化测试当前页面要操作那些元素,就封装那些方法
"""
class PageLogin:
# 输入用户名
def __page_username(self):
pass
# 输入密码
def __page_pwd(self):
pass
# 输入验证码
def __page_verify_code(self):
pass
# 点击登录按钮
def __page_click_login_btn(self):
pass
# 获取昵称
def page_get_nickname(self):
pass
# 组合业务方法 (强调:测试业务成调用此方法,便捷。)
def page_login(self):
pass
配置信息整理
from selenium.webdriver.common.by import By
# 用户名
username = (By.CSS_SELECTOR, "#username")
# 密码
pwd = By.CSS_SELECTOR, "#password"
# 验证码
verify_code = By.CSS_SELECTOR, "#verify_code"
# 登录按钮
login_btn = By.CSS_SELECTOR, ".J-login-submit"
# 昵称
nick_name = By.CSS_SELECTOR, ".userinfo"
# 提示:nick_name = By.CLASS_NAME, ".userinfo" == (By.CLASS_NAME,
".userinfo")
page实现
class PageLogin(Base):
# 输入用户名
def __page_username(self, value):
self.base_input(username, value)
# 输入密码
def __page_pwd(self, value):
self.base_input(pwd, value)
# 输入验证码
def __page_verify_code(self, value):
self.base_input(verify_code, value)
# 点击登录按钮
def __page_click_login_btn(self):
self.base_click(login_btn)
# 获取昵称
def page_get_nickname(self):
return self.base_get_text(nick_name)
# 组合业务方法 (强调:测试业务成调用此方法,便捷。)
def page_login(self, phone, password, code):
self.__page_username(phone)
self.__page_pwd(password)
self.__page_verify_code(code)
self.__page_click_login_btn()
scirpt实现
import unittest
from selenium import webdriver
from page.page_login import PageLogin
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("http://hmshoptest.itheima.net/Home/user/login.html")
self.login = PageLogin(self.driver)
def tearDown(self) -> None:
self.driver.quit()
def
test01_login(self,phone="13600001111",password="123456",code="8888"):
# 调用登录业务
self.login.page_login(phone,password,code)
# 断言
nickname = self.login.page_get_nickname()
print("nickname:", nickname)
数据驱动
理论回顾
什么是数据驱动?
以测试数据驱动用例执行(测试数据和代码分离)
为什么要数据驱动?
便于维护(维护的焦点从代码转到测试数据)
数据驱动如何操作?
1、编写数据存储文件
2、编写读取数据工具
3、使用参数化引用
实际操作
步骤1、编写数据存储文件
口诀:
1、新建json文件,在文件中编写一个{}
2、有几个模块,写几个Key,值为列表
3、列表中参数化数据有几组,就写几个{}
3、每个{}中组成->说明+参数数据+预期结果
{
"login": [
{
"desc":"登录成功",
"phone": "13600001111",
"password": "123456",
"code": "8888",
"expect_text": "13600001111"
}
]
}
步骤2、读取工具封装
def read_json(filename,key):
filepath = os.path.dirname(__file__) + os.sep + "data" + os.sep +
filename
arr = []
with open(filepath,"r",encoding="utf-8") as f:
for data in json.load(f).get(key):
arr.append(tuple(data.values())[1:])
return arr
步骤3、参数化引用
import unittest
from time import sleep
from parameterized import parameterized
from selenium import webdriver
from page.page_login import PageLogin
from util import read_json
class TestLogin(unittest.TestCase):
def setUp(self) -> None:
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.get("http://hmshoptest.itheima.net/Home/user/login.html")
self.login = PageLogin(self.driver)
ddd
def tearDown(self) -> None:
self.driver.quit()
@parameterized.expand(read_json("login.json", "login"))
def test01_login(self, phone, password, code, expect_text):
try:
# 调用登录业务
self.login.page_login(phone, password, code)
# 断言
nickname = self.login.page_get_nickname()
print("nickname:", nickname)
self.assertEqual(nickname, expect_text)
except Exception as e:
print("错误!",e)
APP测试相关环境
工具说明
app自动化执行原理
app类型(技术)
环境搭建
所需环境
jdk
android-sdk
appium
模拟器
1、Jdk安装
说明:为什么要安装jdk?
1、安卓应用或开发工具是使用java语言开发,必须使用jdk
2、android-sdk
说明:android开发工具包
1、解压到指定目录
2、将目录添加到path中
1、新建环境变量:ANDROID_HOME=D:\Android\sdk (这里为安装目录)
2、添加path路径,在Path中添加:%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools;
提示:tools有查看元素工具,我们必须使用;platform-tools是adb命令工具所在目 录。
3、appium安装
说明: 需要安装appium服务端程序和python中调用的api库
服务端:
作用:将脚本发送给手机
安装:双击安装程序 appium-desktop-setup-1.8.0.exe ,一直到完成即可。
python的appium. api库
作用:自动化测试使用api
安装: pip install Appium-Python-Client==1.2.0
4、模拟器
说明:安卓手机
安装: 雷电、mumu、夜神,默认安装完成即可
adb工具
说明:通过电脑,操作android系统的工具
adb工作原理
adb命令
获取包名和启动名
包名: 一个安卓应用的唯一标识符,操作那个应用需要依赖包名
启动名: 应用中界面标识符,允许重复。
1、mac/linux: adb shell dumpsys window | grep usedApp
2、windows: adb shell dumpsys window | findstr usedApp
上传和下载命令
上传: adb push 路径\xxx.txt /sdcard
下载: adb pull /sdcard/xxx.txt 本地文件夹路径
启动时间命令
命令: adb shell am start -W 包名/启动名
注意:查看时间 一般要冷启动(应用程序没有启动)
冷启动:应用程序未启动
热启动:应用程序已启动在后台或当前页面
查看日志
命令: adb logcat > d:\xxx.log
提示: 对app操作时,要开启日志,记录app操作的步骤和异常。
其他常用命令
提示:
1、adb start-server正常不需要手动启动,自动启动adb.exe进程。当应用进程死机,
需要执行杀服务,杀完后需要执行命令启动。
2、adb connect ip:端口 正常不要手动连接,系统会自动连接。如果执行adb devices
没有看到设备列表,需要手动 连接。
查看元素定位信息
为什么要查看元素信息?
说明:自动化测试就是查找元素操作元素,要查找元素,就需要根据元素的信息来查找(id、
class、text、…)
如何查找?
使用:android SDK 自带工具:uiautomatorviewer工具
如何使用?
1、启动:
2、截屏查看
基础操作api
前置:必须启动appium服务、模拟器
from appium import webdriver
# 定义字典变量
desired_caps = {}
# 字典追加启动参数
desired_caps["platformName"] = "Android"
# 注意:版本号必须正确
desired_caps["platformVersion"] = "7.1.1"
# android不检测内容,但是不能为空
desired_caps["deviceName"] = "192.168.56.101:5555"
desired_caps["appPackage"] = "com.android.settings"
desired_caps["appActivity"] = ".Settings"
# 设置中⽂
desired_caps["nicodeKeyboard"] = True
desired_caps["resetKeyboard"] = True
# 获取driver
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
基础api
启动应用
⽅法: driver.start_activity(包名,启动名)
说明:appium⽀持夸应⽤,可以在操作应⽤中切换到B应⽤
获取当前包名、启动名
⽅法
当前应⽤包名:driver.current_package
当前应⽤启动名:driver.current_activity
"""需求:1、启动设置后,暂停3秒,打开短信应⽤2、打印当前默认应⽤包名启动名
"""
sleep(3)
# 启动短信
driver.start_activity("com.android.messaging",".ui.conversationlist.Conversati
onListActivity")
# 打印包名和启动名
print("当前所在应⽤包名:",driver.current_package)
print("当前所在应⽤启动名:",driver.current_activity)
driver.quit()
"""需求:1、判断百年奥莱是否安装,如果安装进⾏卸载,否则安装2、启动设置界⾯3、置于后台3秒钟4、关闭设置界⾯5、关闭app驱动
"""
# 判断百年奥莱是否安装
if driver.is_app_installed("com.yunmall.lc"):print("百年奥莱app已存在正在卸载...")# 卸载driver.remove_app("com.yunmall.lc")print("卸载百年奥莱app成功!")
else:print("百年奥莱不存在,正在安装...")# 安装driver.install_app("/Users/lgy/PycharmProjects/lgy/bj37-web01/day07-
appium01//bainianaolaitemai_115.apk")print("安装百年奥莱app成功!")
sleep(3)
# 启动设置界⾯
print("启动设置界⾯...")
driver.start_activity("com.android.settings",".Settings")
sleep(3)
print("设置应⽤置于后台3秒钟...")
# 置于后台3秒
driver.background_app(3)
print("关闭设置app...")
# 关闭设置
driver.close_app()
print("获取关闭后的app报名")
print("关闭设置app,后获取包名:",driver.current_package)
sleep(3)
print("退出driver驱动")
driver.quit()
元素定位
单个元素定位
print("使⽤id点击放⼤镜...")
# 使⽤id -> 点击放⼤镜
driver.find_element_by_id("com.android.settings:id/search").click()
sleep(1)
print("使⽤class输⼊搜索hello...")
# 使⽤class 输⼊hell
driver.find_element_by_class_name("android.widget.EditText").send_keys("hello"
)
sleep(1)
print("使⽤xpath点击返回...")
# 点击返回 xpath
driver.find_element_by_xpath("//*
[@class='android.widget.ImageButton']").click()
sleep(1)
print("使⽤name点击搜索...")
# 使⽤name定位
driver.find_element_by_accessibility_id("搜索设置").click()
sleep(3)
driver.quit()
定位⼀组元素
# 1、获取所有id为:android:id/title
els = driver.find_elements_by_id("android:id/title")
for el in els:print("元素id为com.android.settings:id/title的为本内容有:",el.text)
els = driver.find_elements_by_class_name("android.widget.TextView")
for el in els:print("元素class为android.widget.TextView的为本内容有:",el.text)
els = driver.find_elements_by_xpath("//*[contains(@text,'设')]")
for el in els:print("⽂本包含'设'内容有:",el.text)
元素操作
方法:
1、点击:元素.click()
2、输⼊:元素.send_keys()
3、清空:元素.clear()
# 输⼊中⽂ 参数
desired_caps["unicodeKeyboard"] = True
desired_caps["resetKeyboard"] = True
# 1、点击放⼤镜
driver.find_element_by_xpath("//*[@resourceid='com.android.settings:id/search']").click()
# 2、输⼊hello
el = driver.find_element_by_xpath("//*[@resourceid='android:id/search_src_text']")
el.send_keys("hello")
# 3、2s 清空内容
sleep(2)
el.clear()
# 4、5s 输⼊你好
sleep(2)
el.send_keys("你好")
元素获取信息
⽅法
获取⽂本:元素.text
获取⼤⼩:元素.size
获取位置:元素.location
需求
1、打开设置app
2、点击放⼤镜
3、获取搜索⽂本值
4、计算搜索框中⼼触摸点
# 获取 菜单我的⽂本
text = driver.find_element_by_xpath("//*[@resourceid='android:id/search_src_text']").text
print("我的菜单,⽂本值为:",text)
location = driver.find_element_by_xpath("//*[@resourceid='android:id/search_src_text']").location
print("位置:",location)
size = driver.find_element_by_xpath("//*[@resourceid='android:id/search_src_text']").size
print("⼤⼩为:",size)
x= location.get("x")+ (size.get("width")/2)
y = location.get("y")+ (size.get("height")/2)
print("触摸中⼼位置x:{} y:{}".format(x,y))# 获取 菜单我的⽂本
text = driver.find_element_by_xpath("//*[@resourceid='android:id/search_src_text']").text
print("我的菜单,⽂本值为:",text)
location = driver.find_element_by_xpath("//*[@resourceid='android:id/search_src_text']").location
print("位置:",location)
size = driver.find_element_by_xpath("//*[@resourceid='android:id/search_src_text']").size
print("⼤⼩为:",size)
x= location.get("x")+ (size.get("width")/2)
y = location.get("y")+ (size.get("height")/2)
print("触摸中⼼位置x:{} y:{}".format(x,y))
获取元素属性
⽅法: 元素.get_attribute(“属性名”)
# 点击放⼤镜
els = driver.find_elements_by_id("com.android.settings:id/title")
for el in els:print("--"* 50)print("1、enabled属性值为:",el.get_attribute("enabled"))print("2、text属性值为:",el.get_attribute("text"))print("3、content-desc属性值为:",el.get_attribute("name"))print("4、resource-id属性值为:",el.get_attribute("resourceId"))print("5、class属性值为:",el.get_attribute("className"))print("--"* 50)
滑动
swipe(start_x,start_y,end_x,end_y)
特点:精准滑动(基于两个坐标 点滑动)
说明:针对坐标点进⾏操作
driver.swipe(100,1000,100,400,duration=2000)
scroll
特点:滚动(有惯性存在,滚动下不按下第⼀个元素)
说明:针对两个元素进⾏操作
drag_and_drop【推荐】
特点:拖拽(没有惯性,按下开始元素拖拽到指定元素位置)
说明:针对两个元素进⾏精准操作
手势操作
说明:
1、appium中封装了⼿势的操作⽅法,都在TouchAction类中,需要导包
2、所有的⼿势操作,最终都需要调⽤perform()⽅法才能执⾏
轻敲
api: TouchAction(driver).tap(el,x,y).perform()
from appium.webdriver.common.touch_action import TouchAction
wlan = driver.find_element_by_xpath("//*[@text='WLAN']")
TouchAction(driver).tap(wlan).perform()
按下与释放
按下: press(el,x,y)
释放: release()
示例
# 获取WLAN元素
wlan = driver.find_element_by_xpath("//*[@text='WLAN']")
loc = wlan.location
print(loc)
# 效果类似点击
TouchAction(driver).press(x=loc.get("x"),y=loc.get("y")).release().perform()
⻓按
⽅法: long_press(el,x,y)
场景: 在移动应⽤中,有时候需要⻓安才能激活菜单选项
示例:
# 获取WLAN元素
wlan = driver.find_element_by_xpath("//*[@text='WLAN']")
wlan.click()
# 必须暂时⼀定时间 暂停3秒(原因:元素未加载出来,直接⻓按坐标没有任何效果)
sleep(3)
# 效果类似点击
TouchAction(driver).long_press(x=777,y=375).perform()
移动及等待
移动:move_to(el,x,y)
等待: wait()
案例:九宫格解锁
"""需求:绘制解锁图案 Z绘制坐标点:1、1048,340 1377,340 1707,3402、1377,6693、1048,999 1377,999 1707,999
"""
# 1、定位WLAN
WLAN = driver.find_element_by_xpath("//*[@text='WLAN']")
# 2、定位应⽤
app = driver.find_element_by_xpath("//*[@text='应⽤']")
driver.drag_and_drop(app,WLAN)
# 3、点击安全
driver.find_element_by_xpath("//*[@text='安全']").click()
# 4、点击屏幕锁定⽅式
driver.find_element_by_xpath("//*[@text='屏幕锁定⽅式']").click()
# 5、点击图案
driver.find_element_by_xpath("//*[@text='图案']").click()
# 必须暂停,登录绘制⻚⾯加载完成
sleep(2)
# 6、绘制
TouchAction(driver).press(x=1048,y=340).wait(100).move_to(x=1377,y=340).wai
t(100).move_to(x=1707,y=340).wait(100)\.move_to(x=1377,y=669).wait(100).move_to(x=1048,y=999).wait(100).move_to(x=
1377,y=999).wait(100).move_to(x=1707,y=999).wait(100)\.perform()
手机操作api
说明:使⽤appium框架的api操作android
常⽤api
查看当前⽹络类型
设置⽹络类型
查看当前分辨率
截图
扩展⽹络类型
# 1、查看当前⽹络类型
print("当前⽹络类型为:",driver.network_connection)
# 2、设置⽹络类型为⻜⾏模式
driver.set_network_connection(1)
print("设置之后的⽹络类型为:",driver.network_connection)
# 3、获取当前屏幕分辨率
print("当前屏幕分辨率为:",driver.get_window_size())
# 4、截图保存
driver.get_screenshot_as_file("./screen.png")
按键操作
"""需求:1、点击三次⾳量+ 242、点击返回 43、点击两次⾳量- 25"""
i = 0
# 三次增⼤⾳量
while i < 3:driver.press_keycode(24)i += 1
sleep(2)
# 点击返回
driver.press_keycode(4)
i = 0
# 两次减⼩⾳量
while i < 2:driver.press_keycode(25)i += 1# 提示:部分模拟器没有按键操作效果
通知栏
应⽤场景:检查服务器发送的通知
"""需求:1、打开通知栏,点击通知栏的信息
"""
# 打开通知栏
driver.open_notifications()
sleep(2)
# 查找信息并点击
driver.find_element_by_xpath("//*[@text='应⽤宝.apk']").click()
** 获取toast消息**
说明: toast消息为移动应⽤中,⼀种⿊底⽩字提示信息,有时间限制
为什么要获取toast消息?
断⾔内容
如何获取?
步骤
安装依赖库
配置driver启⽤参数
编写代码获取⽂本值
安装依赖库: lpip install uiautomator2
配置driver启⽤参数
desired_caps[‘automationName’] = ‘Uiautomator2’
编写代码获取⽂本值
# 获取toast消息
msg = driver.find_element_by_xpath("//*[contains(@text,'请先勾选同
意')]").text
print("toast消息为:",msg)
切换webview【了解】
app中嵌套web信息,如果不切换⽆法定位操作
如何切换
1、获取当前环境 --> driver.contexts
2、调⽤切换⽅法 --> driver.switch_to.context(“环境”)
# 获取当前所有的环境
print(driver.contexts)
# 切换环境
driver.switch_to.context("WEBVIEW_com.android.browser")
monkey
android系统⾃带⼀种测试压⼒的⼩⼯具,主要测试系统或应⽤稳定性
测试是否崩溃、闪退、死机
adb shell monkey -p com.yunmall.lc -v -v 10000 >xxx.log
-p:包名
-v -v:⽇志
10000:乱抓的时间次数
-s:设置随机种⼦数(两次执⾏随机种⼦数⼀样,执⾏的事件也是⼀样)
其他参数
搜索:“ANR”、ERROR、null、相关错误
练习案例
(appium+selenium+po封装)
实现的目标:1、app下单 2、web后台端进行发货
tpshop下单业务自动化业务实现(app)
实现效果
项目实施
要实现的业务:app下单流程
实现技术:python+appium+selenium
结构实现:po+日志+数据驱动+报告
执行方式:持续集成
结构搭建
import os
import time
from selenium.webdriver.support.wait import WebDriverWait
from config import DIR_PATH
class Base:
# 初始化方法
def __init__(self, driver):
self.driver = driver
# 查找元素方法
def base_find(self, loc, timeout=10, poll=0.5):
return WebDriverWait(self.driver, timeout, poll).until(lambda x:
x.find_element(*loc))
# 点击方法
def base_click(self, loc):
self.base_find(loc).click()
# 输入方法
def base_input(self, loc, value):
# 获取元素
el = self.base_find(loc)
# 清空
el.clear()
# 输入
el.send_keys(value)
# 获取文本方法
def base_get_text(self, loc):
return self.base_find(loc).text
# 截图方法
def base_get_img(self):
img_path = DIR_PATH + os.sep + "img" + os.sep + "
{}.png".format(time.strftime("%Y%m%d%H%M%S"))
self.driver.get_screenshot_as_file(img_path)
app登录page结构
class PageAppLogin:
# 1、点击 我的
def page_app_click_me(self):
pass
# 2、点击 登录头像
def page_app_click_login_link(self):
pass
# 3、输入用户名
def page_app_input_username(self):
pass
# 4、输入密码
def page_app_input_pwd(self):
pass
# 5、点击勾选协议
def page_app_click_pro(self):
pass
# 6、点击登录按钮
def page_app_click_login_btn(self):
pass
# 7、获取登录昵称
def page_app_get_nickname(self):
pass
# 8、组合业务方法
def page_app_login(self):
pass
"""
一、以下为app登录模块配置信息
"""
# 我的
app_login_me = By.XPATH, "//*[@text='我的']"
# 登录图片(连接)
app_login_link = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/head_img']"
# 用户名
app_username = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/mobile_et']"
# 密码
app_pwd = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/pwd_et']"
# 协议
app_pro = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/agree_btn']"
# 登录按钮
app_login_btn = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/login_tv']"
# 昵称
app_nickname = By.XPATH, "//*[@resource-id='com.tpshop.malls:id/nick_name_tv']"
** 登录script实现**
import unittest
from page.page_app_login import PageAppLogin
from util import GetDriver
class TestAppLoginOrder(unittest.TestCase):
def setUp(self) -> None:
self.driver = GetDriver.get_app_driver()
# 获取PageAppLogin实例
self.app = PageAppLogin(self.driver)
def tearDown(self) -> None:
self.driver.quit()
def test01_login_order(self):
# 调用登录
self.app.page_app_login()
# 打印昵称->断言
nickname = self.app.page_app_get_nickname()
print("登录账户昵称为:", nickname)
# 提示:获取昵称需要加个强制等待时间 2
订单业务结构搭建(page_order.py)
class PageAppOrder:
# 点击首页
def page_app_click_index(self):
pass
# 点击搜索框
def page_app_click_search_text(self):
pass
# 输入搜索内容
def page_app_input_search_text(self):
pass
# 点击搜索按钮
def page_app_click_search_btn(self):
pass
# 选择商品
def page_app_select_photo(self):
pass
# 点击加入购物车
def page_app_add_cart(self):
pass
# 点击确定
def page_app_cart_ok(self):
pass
# 点击购物车
def page_app_click_cart(self):
pass
# 点击立即购买
def page_app_now_purchase(self):
pass
# 点击提交订单
def page_app_click_submit_order(self):
pass
# 点击 立即支付
def page_app_click_now_pay(self):
pass
# 输入 密码
def page_app_input_pwd(self):
pass
# 点击确定
def page_app_click_sure(self):
pass
# 获取订单编号
def page_app_get_order_on(self):
pass
# 下单业务方法
def page_app_order(self):
pass
web后台实现发货业务(今日实现)
持续集成->日志->报告->数据驱动
Base类
说明:由于web发货页面需要切换iframe标签,所以在Base类中新增两个方法(1、切换frame
2、回到默认目录)
# 切换frame
def base_switch_frame(self, loc):
# 获取元素
el = self.base_find(loc)
# 执行切换
self.driver.switch_to.frame(el)
# 恢复frame
def base_default_frame(self):
self.driver.switch_to.default_content()
Page类
结构搭建(page_web_login.py)
class PageWebLogin:
# 1、输入用户名
def page_web_username(self):
pass
# 2、输入密码
def page_web_pwd(self):
pass
# 3、输入验证码
def page_web_verify_code(self):
pass
# 4、点击登录按钮
def page_web_login_btn(self):
pass
# 5、获取登录昵称
def page_web_nickname(self):
pass
# 登录业务方法
def page_web_login(self):
pass"""
三、web后台登录配置信息整理
"""
# 用户名
web_login_username = By.CSS_SELECTOR, "[name='username']"
# 密码
web_login_pwd = By.CSS_SELECTOR, "[name='password']"
# 验证码
web_login_verify = By.CSS_SELECTOR, "[name='vertify']"
# 登录按钮
web_login_submit = By.CSS_SELECTOR, "[name='submit']"
# 昵称
web_login_nickname = By.CSS_SELECTOR, ".bgdopa-t"
driver封装
# 获取Web Driver
@classmethod
def get_web_driver(cls):
if cls.__web_driver is None:
cls.driver = webdriver.Chrome()
cls.driver.get(HOST)
cls.driver.maximize_window()
return cls.driver
发货Page页面实现
import page, time
from base.base import Base
class PageWebOrder(Base):
# 订单菜单
def page_web_order_menu(self):
self.base_click(page.web_order)
# 左侧 发货单
def page_web_order_goods(self):
self.base_click(page.web_order_goods)
# 工作区域 去发货
def page_web_go_goods(self):
# 切换iframe
self.base_switch_frame(page.web_order_iframe)
self.base_click(page.web_order_go_goods)
# 物流公司
def page_order_company(self):
self.base_click(page.web_order_company)
# 配送单号
def page_order_input_order_no(self):
value = str(time.strftime("%Y%m%d%H%M%S"))
self.base_input(page.web_order_order_no, value)# 确认发货
def page_order_goods_ok(self):
self.base_click(page.web_order_goods_ok)
# 打印配置单
def page_order_print_order(self):
self.base_click(page.web_order_print_order)
# 获取订单编号
def page_order_get_on(self):
return self.base_get_text(page.web_order_on)
# 订单发货业务
def page_order_go_goods(self):
self.page_web_order_menu()
self.page_web_order_goods()
self.page_web_go_goods()
self.page_order_company()
self.page_order_input_order_no()
self.page_order_goods_ok()
self.page_order_print_order()
Script类
test02_web_login_order.py
登录
import unittest
from time import sleep
from page.page_web_login import PageWebLogin
from util import GetDriver
class TestWebLoginOrder(unittest.TestCase):
def setUp(self) -> None:
self.driver = GetDriver.get_web_driver()
self.login = PageWebLogin(self.driver)
def tearDown(self) -> None:
sleep(1)
self.driver.quit()
def test01_web_login_order(self):
self.login.page_web_login()
nickname = self.login.page_web_nickname()
print("登录后的昵称为:",nickname)
订单配置信息整理
# 订单菜单
web_order = By.XPATH, "//a[text()='订单']"
# 左侧菜单 发货单
web_order_goods = By.XPATH, "//a[text()='发货单']"
# iframe
web_order_iframe = By.CSS_SELECTOR, "#workspace"
# 去发货 //div[text()='202112161517008312']/../..//td[@class='handle']//a[1]
web_order_go_goods = By.XPATH, "//a[text()='去发货']"
# 物流公司
web_order_company = By.CSS_SELECTOR, "[value='YZPY']"
# 配送单号
web_order_order_no = By.CSS_SELECTOR, "#invoice_no"
# 确认发货
web_order_goods_ok = By.CSS_SELECTOR, "ncap-btn-send"
# 打印配置单
web_order_print_order = By.CSS_SELECTOR, ".fa-print"
# 获取订单编号
web_order_on = By.XPATH, "//div[@id='printDiv']/div[@class='contactinfo']/dl[1]/dd[2]"
# 难点:如何根据指定单号,查找对应的去发货元素?
# 思路:想找共同的父级,在逐级查找
订单发货
import unittest
from time import sleep
import page
from page.page_web_login import PageWebLogin
from page.page_web_order import PageWebOrder
from util import GetDriver
class TestWebLoginOrder(unittest.TestCase):
def setUp(self) -> None:
self.driver = GetDriver.get_web_driver()
self.login = PageWebLogin(self.driver)
self.order = PageWebOrder(self.driver)
def tearDown(self) -> None:
sleep(1)
self.driver.quit()
def test01_web_login_order(self):
try:
# 登录
self.login.page_web_login()
nickname = self.login.page_web_nickname()
print("登录后的昵称为:", nickname)
# 发货
self.order.page_order_go_goods()
# 打印订单
order_no = self.order.page_order_get_on()
print("发货的订单为:", order_no)
# self.assertEqual(page.order_on,order_no)
except Exception as e:
print("错误原因:",e)
raise
日志封装
# 日志封装
class GetLog:
__log = None
@classmethod
def get_log(cls):
if cls.__log is None:
# 获取日志器
cls.__log = logging.getLogger()
# 设置入口级别
cls.__log.setLevel(logging.INFO)
# 获取处理器
filename = DIR_PATH + os.sep + "log" + os.sep +
"tpshop_auto.log"
tf =
logging.handlers.TimedRotatingFileHandler(filename=filename,
when="midnight",
interval=1,
backupCount=3,
encoding="utf-8")
# 获取格式器
fmt = "%(asctime)s %(levelname)s [%(filename)s(%(funcName)s:%
(lineno)d)] - %(message)s"
fm = logging.Formatter(fmt)
# 将格式器添加到处理器
tf.setFormatter(fm)
# 将处理器添加到日志器
cls.__log.addHandler(tf)
# 返回日志器
return cls.__log
#日志应用
1、记录程序运行步骤 base->info
2、记录程序错误 script->error
持续集成
报告无样式问题
System.setProperty(“hudson.model.DirectoryBrowserSupport.CSP”, “”)
这篇关于测试技能整理HM-UI自动化测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!