自动化测试,包括Web自动化、移动端自动化、接口自动化等,目前暂时先把web自动化的笔记放在这里,而且只放到PO模式的笔记,这些笔记很多知识点都是有实例的,不想就这样子丢了,还是先保存好。后面还有一些,过两天补上:
一、什么样的项目需要做自动化测试:
1、需求变动不频繁
2、项目周期长
3、项目需要回归测试
二、什么阶段开始:
功能测试完毕(手工测试)
三、web自动化测试所属分类:
1、黑盒测试(功能)-----自动化属于这个
2、白盒测试(单元)
3、灰盒测试(接口)
web自动化测试用例:跟着功能测试的用例后面加上“是否自动化”
四、安装selenium包
1、****很重要的:pip包管理工具
按Ctrl+r跑出cmd的运行窗口,然后输入pip install selenium 就会安装最新版本的selenium
安装完后可以输入pip show selenium来查看版本等信息
可以输入 pip uninstall selenium来卸载
扩展:
pip install selenium==版本号来指定安装的版本
在==号后面输入一个错误的版本号按回车,会提示所有可以安装的版本号
==号后面抄一个正确的版本就可以了
pip是python中的包管理工具(可以安装、卸载、查看python工具)-------需要先安装好python3的运行环境
必须连网才能使用Pip
pip list:查看通过pip工具安装的插件或工具
2、可以通过pycharm来安装
推荐原因:安装到当前工程环境里
操作:File--setting--Project:当前工程名称---Project Interpreter--点击+号安装相应包和版本
提示:如果使用pip install 插件名 安装过后,打开pycharm,导包操作时,提示找不到此包,那就
说明使用pip install 默认安装 的路径和当前工程所有的环境路径不是同一个环境,进行以上处理即可
安装浏览器驱动
firefox 48以上版本 需要selenium 3.x+firefox驱动(geckodriver)
下载地址:https://github.com/mozilla/geckodriver/releases/
里面有各个版本的驱动,有相对应的firefox版本
驱动和浏览器版本是对应的,下载哪一个要去官网查看
google
http://chromedriver.storage.googleapis.com/index.html
应用:
将浏览器驱动放到指定文件夹
将浏览器驱动所在目录添加到系统path环境变量中
科普path:
如果在cmd里面输入命令时提示不是内部命令,一般都是path这里没设置好
说明:指定系统搜索的目录
dos命令默认搜索顺序:1、检测是否为内部命令 2、检测是否为当前目录下可执行文件
3、检测path环境变量指定的目录
提示:
1、如果 以上搜索目录都检测不到输入的命令或或执行文件,系统会抛出不是内部或外部命令。。。
2、在web环境中,如果不将浏览器驱动添加到path中,selenium在运行的时候会提示浏览器驱动有误
# 第一个selenium案例:
# 需求:打开百度首页并停留3秒后关闭浏览器驱动
#1、导包
from selenium import webdriver
from time import sleep
#获取浏览器驱动
driver=webdriver.Chrome()
#打开url
driver.get("http://baidu.com")
#暂停3秒
sleep(3)
#关闭浏览器驱动
driver.quit();
五、元素定位
工具:
Firefox Firebug(F12获取)
谷歌 F12
元素定位依赖于:1、标签名 2、属性 3、层级 4、路径
定位方式:
元素属性
1、id
2、name
3、class_name
标签
4、tag_name
a标签 (可点击文本)
5、link_text(定位超连接 a标签)
6、partial_link_text(同上,可模糊)
7、XPath(基于元素路径)
8、CSS(元素选择器)
1、id定位
element=driver.find_element_by_id(id)
例子:
from selenium import webdriver
from time import sleep; #如果直接import time会把time的所有方法都导进来,不好
#快速导包:输入sleep后,按ctrl+alt+空格,然后选中想要的方法后回车,就直接导包成功了
driver=webdriver.Firefox();
#如果输入的是本地文件的url,例如E:\aaa\bbb\ccc\index.html
#里面有\反斜杆,其在python中是转义字符。此时可以写成url=r"E:\aaa\bbb\ccc\index.html" r是装饰的字符串,如果字符串中有转义字符,不进行转义使用
#也可以不用r,直接写2个\\ E:\\aaa\\bbb\\ccc\\index.html
#使用本地浏览器模式 前缀必须添加file:///
#url="file:///E:/aaa/bbb/ccc/index.html"
url='http://www.baidu.com'
driver.get(url);
driver.maximize_window();
driver.find_element_by_class_name('engine-key-wrapper').send_keys('python');
driver.find_element_by_id('search-submit').click();
sleep(3)
driver.quit();
2、name定位
element=driver.find_element_by_name(name) #name是用引号括起来的
3、class_name定位
element=driver.find_element_by_class_name(classname)
4、tag_name定位 标签即是元素 例如input div a 等 很少用
element=driver.find_element_by_tag_name(tag_name)
如果找到多个同样的的标签,默认返回第一个
5、link_text定位和partial_link_text定位 封装的时候一般使用后者
都是定位a标签,但是partial_link_text可以模糊定位,即不用全部匹配,前者需要
element=driver.find_element_by_link_text(text) # 引号内部的文字如果不是全部匹配则会报错
element=driver.find_element_by_partial_link_text('123').click(); #只有是包含123的字符的就可以click了。但是最好使用能代表唯一性的词,如果有多个,还是返回第一个
7、XPath XML Path
element=driver.find_element_by_xpath(xpath)
定位策略(方式)
1)路径 看7.1路径定位
2)利用元素属性 比如//input[@id='password'] 这是相对路径定位时,可能会找到多个input,此时考虑结合属性来定位,需要[@]
3)属性加上逻辑 比如//input[@class='login' and @name='user-test'] 当使用一个属性class找到的还不是唯一时,需要使用and 来多加一个属性来找
4)层级与属性结合 比如://p[@id='login-T2']/input[@name='user' and @type='text'] 当使用2个属性都找不到唯一时,给它加上父层级路径 第一次要//,第二级就只要/
一般使用子级的属性找不到唯一,就加上父级的属性,然后把子级的属性去掉:
//p[@id='login-T2']/input
7.1 路径定位(绝对 、相对路径)
绝对路径:从最外层元素到指定元素之间所有经过元素层级的路径
1)绝对路径以/html根节点开始,使用/来隔元素层级:
/html/body/div/fieldset/p[1]/input
2)绝对路径对页面结构要求比较严格,不建议使用
相对路径:匹配任意层级的元素,不限制元素的位置
1)相对路径以//开始
2)格式://input 或者 //*
7.2 XPath延申
//*[text()='xxx'] 文本内容是xxx的元素
driver.find_element_by_xpath("//*[text()='开课/合作']").click();
//*[contains(@attribute,'xxx')] 属性中含有xxx的元素 移动端全是这样子写,重要
driver.find_element_by_xpath("//*[contains(@id,'keyword')]").send_keys('python');
//*[starts-with(@attribute,'xxx')] 属性以xxx开头的元素
driver.find_element_by_xpath("//*[starts-with(@id,'js_sea')]").click();
8、CSS定位方法
element=driver.find_element_by_css_selector(css_selector)
8.1、常用策略
1) id选择器 #id
2)class选择器 .class
3)元素选择器 input 直接写属性名称
4)属性选择器 [name='passwordA']
5)层级选择器 p>input 或者 p input 两者的区别:p一定是input的直接上级,后者则不一定是直接上级,不管中间隔多少级
p#idname>input id名称为idname的p下面的input
driver.find_element_by_css_selector('.header-index-text p').click(); 层级与元素选择器、class选择器结合
使用CSS 元素选择器 定位span(这个是任意标签名)标签获取文本值
下面就是打印出class=mnav的标签的文本
ps=driver.find_element_by_css_selector('.mnav').text;
print('文本值是:',ps) #文本值是: 抗击肺炎
8.2 CSS 延申
input[type^='p'] type属性以p字母开头的元素
input[type$='d'] type属性以d字母结束的元素
input[type*='w'] type属性包含d字母的元素
8.3 定位一组元素
dirver.find_elements_by_id
#以下是定位到一组以mnav为class名的元素列表,打印其长度为7
elements=driver.find_elements_by_css_selector('.mnav')
print(len(elements));
#那么要对其中一个进行操作,需要以下标的方式
elements[1].click();
#通过遍历来操作,假设是文本框,全部输入同一个文字信息
for el in elements:
el.send_keys('我是同一个文字信息');
8.4 find_element方法封装
# 需求:使用driver_element方法来定位
driver.find_element(By.ID,'kw').send_keys('python');
driver.find_element(By.CSS_SELECTOR,'#su').click();
By类:需要导包位置 输入By之后,按ctrl+alt+空格可以快速导包
from selenium.webdriver.common.by import By
六、元素操作方法
1、目的:
1)需要让脚本模拟用户级指定元素插入值
2)模拟人为删除元素的内容
3)模拟点击操作
2、方法
1) click() 单击元素
2)send_keys(value) 模拟输入
3)clear() 清除文本
3、案例
需求:输入文本后清空、修改再提交
driver.find_element_by_css_selector('#kw').send_keys("python");
sleep(3);
driver.find_element_by_css_selector('#kw').clear();
driver.find_element_by_css_selector('#kw').send_keys("java");
sleep(3)
driver.find_element_by_css_selector('#su').click();
七、浏览器操作方法
最大化 maximize_window()
设置大小 set_window_size(width,height)
窗口位置 set _window_position(x,y)
后退 back()
前进 forward()
刷新 F5 refresh()
关闭当前窗口 close()
关闭由driver启动的所有窗口 quit()
获取页面的title title
获取当前的url current_url
提示:最后两个,没有括号,应用场景:一般为判读上步操作是否成功
实例:
from selenium import webdriver;
from time import sleep;
driver=webdriver.Firefox();
# driver._is_remote = False
url='http://www.baidu.com';
driver.get(url);
#将浏览器最大化
driver.maximize_window();
sleep(5)
# 设置固定大小300、200
# driver.set_window_size(300,200);
# sleep(2)
# 移动浏览器窗口位置 x:320 y:150
# driver.set_window_position(320,150)
# sleep(2)
# 最大化
# driver.maximize_window();
# 点击访问另一个访问,演示后退功能
driver.find_element_by_name('tj_trnews').click();
# driver.find_element_by_css_selector('').click();
sleep(2)
driver.back();
sleep(2)
# 然后演示前进功能
#driver.forward();
#sleep(3);
driver.find_element_by_css_selector('#kw').send_keys('python');
sleep(3);
#刷新
driver.refresh();
#获取当前页面title
title=driver.title;
print('当前页面的title是:',title);
sleep(3)
#获取当前页面url
current_url=driver.current_url;
print('当前页面的url是:',current_url);
sleep(3);
driver.find_element_by_partial_link_text('关于百度').click();
sleep(3);
driver.close();
sleep(3);
driver.quit();
八、获取元素信息
1、元素的文本
2、元素的属性值 作用:检测属性值的正确与否、 利用属性值判断是否我们要的元素
3、判断元素是否可见状态
常用方法
1、size 返回元素大小
2、text 获取元素的文本 size和text是属性,调用时无括号:xxx.size
3、get_attribute('xxx') 获取属性值,传递的参数为元素的属性名
4、is_displayed() 判断元素是否可见
5、is_enabled() 判断元素是否可用
6、is_selected() 判断元素是否选中,用来检查复选框或单选按钮是否被选中
实例:
from selenium import webdriver;
from time import sleep;
driver=webdriver.Firefox();
# driver._is_remote = False
url='http://www.baidu.com';
driver.get(url);
#将浏览器最大化
driver.maximize_window();
#获取输入框的大小
# 获取页面上某个超连接的文本内容
# 获取页面上第一个超链接的地址
# 判断页面中的某标签是否可见
# 判断页面中的某按钮是否可用
# 判断页面f中的复选框是否为选中的状态
size=driver.find_element_by_css_selector('#kw').size;
print('输入框的大小是:',size);
texts=driver.find_element_by_css_selector('#virus-2020').text;
print('页面中id为virus-2020的元素的文字为:',texts);
att=driver.find_element_by_css_selector('a').get_attribute('text')
print('页面中id是kw的属性是:',att);
display=driver.find_element_by_css_selector('#su').is_displayed();
print('搜索按钮是否可见:',display);#结果是True或者False
enabled=driver.find_element_by_css_selector('#su').is_enabled();
print('搜索按钮是否可用:',enabled);#结果是True或者False
# selected=driver.find_element_by_css_selector('xxx').is_selected();
# print('xxx是否被选中',selected);
driver.quit();
结果是:
输入框的大小是: {'width': 548.0, 'height': 44.0}
页面中id为virus-2020的元素的文字为: 抗击肺炎
页面中id是kw的属性是: 百度首页
搜索按钮是否可见: True
搜索按钮是否可用: True
九、键盘和鼠标操作
鼠标:点击、右击、双击、悬停、拖拽
1、鼠标操作的方法
说明:在Selenium中将操作鼠标的方法封闭在ActionChains 类中
实例化对象:action=ActionChains(driver)
方法:
1) context_click(element) 右击
2) double_click(element) 双击
3)drag_and_drop(source,target) 拖动
4) move_to_element(element) 悬停
5) perform() 执行 ------>此方法用来执行以上所有鼠标操作
提示:selenium框架中虽然提供了右击鼠标的方法,但是没有提供选择右击菜单方法,可以通过发送快捷键的方式解决(但是chrome浏览器不支持这种方法)
实例:
from selenium import webdriver;
from time import sleep;
from selenium.webdriver import ActionChains
driver=webdriver.Chrome();
# driver._is_remote = False
url='http://www.baidu.com';
driver.get(url);
#将浏览器最大化
driver.maximize_window();
#首先实例化并获取ActionChains类 先输入ActionChains然后按ctrl+alt+空格键导包from selenium.webdriver import ActionChains
action=ActionChains(driver);
#在文本框里右击, 预期:出现“粘贴”功能
# action.context_click(driver.find_element_by_css_selector('#kw')).perform();
# sleep(5)
#先手动复制一个文本,然后使用发送右键快捷键的方式粘贴一个内容
serchtext=driver.find_element_by_css_selector('#kw');
action.context_click(serchtext).perform();
serchtext.send_keys('p');#p是右键菜单粘粘的快键键
#在文本框里输入文字后双击 预期:全选中
ele=driver.find_element_by_css_selector('#kw');
action.context_click(ele).perform();
#移动到超连接后悬停 预期:超连接文字变色
action.move_to_element(driver.find_element_by_css_selector('#virus-2020')).perform();
sleep(5);
action.double_click(driver.find_element_by_css_selector('#kw').send_keys('python')).perform();
sleep(5)
driver.find_element_by_css_selector('#virus-2020').click();
sleep(5)
#拖拽
sourse=driver.find_element_by_css_selector('#div1');#源元素;假设是一个红色盒子
target=driver.find_element_by_css_selector('#div2');#目标元素
action.drag_and_drop(sourse,target).perform();
#拖拽扩展
action.drag_and_drop_by_offset(sourse,xoffset=360,yoffset=180);#拖动往x轴360个像素,y轴180个像素
driver.quit();
2、键盘操作
#Selenium中把键盘按钮都封装在keys类中
1)删除键(BackSpace) send_keys(Keys.BACK_SPACE)
2)空格键(Space) send_keys(Keys.SPACE)
3)制表键(Tab) send_keys(Keys.TAB)
4)回退键(Esc) send_keys(Keys.ESCAPE)
5) 回车键(Enter) send_keys(Keys.ENTER)
6)全选(Ctrl+A) send_keys(Keys.CONTROL,'a')
7)复制(Ctrl+C) send_keys(Keys.CONTROL,'c')
实例:
from selenium import webdriver;
from time import sleep;
from selenium.webdriver.common.keys import Keys;
driver=webdriver.Chrome();
url='http://www.baidu.com'
driver.get(url);
"""
需求
输入框里输入python1
删除1
全选 复制 python
粘贴
"""
serchtext=driver.find_element_by_css_selector('#kw');
serchtext.send_keys('python1');
sleep(3);
serchtext.send_keys(Keys.BACK_SPACE);
sleep(3)
serchtext.send_keys(Keys.CONTROL,'a');
serchtext.send_keys(Keys.CONTROL,'c')
sleep(3)
serchtext.send_keys(Keys.CONTROL,'v')
sleep(3)
driver.find_element_by_css_selector('#su').click();
sleep(5)
driver.quit();
十一、元素等待
由于电脑配置或网络原因,在查找元素时,元素代码未在第一时间内被 加载出来,而抛出未找到元素异常
概念:元素在第一次未找到时,元素等待设置的时长被激活,如果在设置的有效时长内找到元素,继续执行代码,如果超出设置的时间未找到元素,抛出未找到元素异常
分类:
隐式等待:定位元素时,如果能定位到元素则直接返回元素,不触发等待;如果不能定位到该元素,则间隔一段时间后再去定位元素;如果在达到最大时长还没找到指定元素,则抛出不存在的异常 NoSuchElementException
现实方式:
driver.implicitly_wait(timeout) 单位是秒。实际工作中一般设置为30秒,但是如果是新浪网实种网页,直接设置80秒
实例:
from selenium import webdriver;
from time import sleep;
from selenium.webdriver.common.keys import Keys;
driver=webdriver.Chrome();
#设置隐式等待10秒。
# 特色:1、设置在全局中,针对所有元素有效 2、一般情况下为前置必写代码(1)获取浏览器驱动对象;2)最大化浏览器;3)设置等待)
driver.implicitly_wait(10);
url='http://www.baidu.com'
driver.get(url);
"""
需求
隐式等待使用
给一个错误的Id,不能知道,如果直接抛出异常,说明等待失败,如果在设置的时间外抛出异常则说明等待设置成功
"""
serchtext=driver.find_element_by_css_selector('#kw');
serchtext.send_keys('python1');
sleep(3);
driver.find_element_by_css_selector('#su').click();
sleep(5)
driver.quit();
显式等待:
定位元素时,如果能定位到元素则直接返回元素,不触发等待;如果不能定位到该元素,则间隔一段时间后再去定位元素;如果在达到最大时长还没找到指定元素,则抛出异常 TimeoutException
在Selenium中把显式等待的相关方法封装在WebDriverWait 类中
现实方式:
1、导包 等待类
from selenium.webdriver.support.wait import WebDriverWait;
2、实例化
WebDriverWait(driver,timeout=30,poll_frequency=0.5)
1)driver:浏览器驱动对象
2)timeout:超时时长,单位:秒
3)poll_frequency:检测间隔时间:默认为0.5秒,可以改为任意秒
3、调用方法
until(method):直到。。。时
1)method:函数名称,该函数用来实现对元素的定位
2)一般使用匿名函数来实现:lanbda x:x.find_element_by_id('kw')
4、element=WebDriverWait(driver,10,1).until(lambda x:x.find_element_by_id('kw'))
#x:x为driver,它是webDriverWait类将传入的driver赋值给类self._driver,until方法调用了self._driver;
提示:WebDriverWait(driver,timeout=30,poll_frequency=0.5).until(lambda x:x.find_element_by_id('kws'))返回一个元素
实例:
from selenium import webdriver;
from time import sleep;
from selenium.webdriver.common.keys import Keys;
driver=webdriver.Chrome();
#导显式等待的包
from selenium.webdriver.support.wait import WebDriverWait;
url='http://www.baidu.com'
driver.get(url);
#实例化WebDriverWait()并调用
#注意:调用until方法返回的一定是一个元素
el=WebDriverWait(driver,timeout=30,poll_frequency=0.5).until(lambda x:x.find_element_by_id('kws'));
el.send_keys('python');#注意:此时el还不是元素,只有代码运行起来的时候才是元素,所以写el.不会弹出send_keys();
sleep(3)
"""
需求
显式等待使用
给一个错误的Id,不能知道,如果直接抛出异常,说明等待失败,如果在设置的时间外抛出异常则说明等待设置成功
"""
driver.find_element_by_css_selector('#su').click();
sleep(5)
driver.quit();
显式等待和隐式等待的区别:
1、显式等待:针对单个元素生效
2、隐式等待:针对全局元素生效
十二、扩展:上传文件
from selenium import webdriver;
from time import sleep;
from selenium.webdriver.common.keys import Keys;
driver=webdriver.Chrome();
url='http://www.baidu.com'
driver.get(url);
"""
需求:点击上传文件按钮,可以选择文件上传的
错误思路:
假设【浏览】按钮的name是upfile
driver.find_element_by_css_selector("[name='upfile']").click()
这种思路,点开浏览按钮后弹出的窗口不是浏览器了,无法操作上传文件
"""
#正确的实现方式:使用send_keys('文件路径及文件名')
driver.find_element_by_css_selector("[name='upfile']").send_keys('D:\hello123.txt');#这样子就能上传成功了
sleep(5)
driver.quit();
十三、使用CSS定位下拉框 select标签 option
实例:
方式一:
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
url='https://mail.163.com/register/index.htm?from=163mail';
driver.get(url);
driver.maximize_window();
driver.implicitly_wait(30);
"""
需求:163邮箱注册页面,下拉选择框默认选中163.com
暂停2秒
1、定位 126.com 然后暂停2秒
2、定位yeah.net
"""
driver.find_element_by_css_selector('#username').send_keys('wushuirong_123');
driver.find_element_by_css_selector('.domain').click();
sleep(2);
driver.find_element_by_css_selector("[data-value='126.com']").click();
sleep(2);
driver.find_element_by_css_selector('.domain').click();
driver.find_element_by_css_selector("[data-value='yeah.net']").click();
sleep(2)
driver.quit();
方式二、使用Select方法
步骤:
1、导包 Select类 from selenium.webdriver.support.select import Select;
2、获取Select对象
匿名:Select(element).select_by_index() #通过下标
实名:select=Select(element)
调用:select.select_by_index();
注意:
1、Select类是通过select标签来控制其下的option元素
2、element:只能是select标签
3、实例化select时,需要的参数为select标签元素
4、调用Select类下面的方法,是通过索引、value值或显示文本去控制,而不需要click()事件。
element:标签对应的元素,通过元素定位方式获取,例如:
driver.find_element_by_id('selectA')
操作方式:
1、select_by_index(index) 根据option索引来定位,从0开始
2、select_by_value(value) 根据option属性value值定位
3、select_by_visible_text(text) 根据option显示文本来定位
实例:
from selenium import webdriver;
from time import sleep;
from selenium.webdriver.support.select import Select;
driver=webdriver.Chrome();
url='暂时没有实例连接';
driver.get(url);
driver.maximize_window();
driver.implicitly_wait(30);
"""
假设代码是这样子的:
北京
上海
广州
重庆
需求是:
暂停2秒
1、定位 上海
2、暂停2秒
3、定位广州
"""
sleep(2)
#通过下标形式访问
Select(driver.find_element_by_css_selector('#selectA')).select_by_index(1);
sleep(2)
Select(driver.find_element_by_css_selector('#selectA')).select_by_index(2);
sleep(2)
#通过value值形式访问
Select(driver.find_element_by_css_selector('#selectA')).select_by_value('sh');
sleep(2)
Select(driver.find_element_by_css_selector('#selectA')).select_by_value('gz');
#通过文本值形式访问
Select(driver.find_element_by_css_selector('#selectA')).select_by_visible_text('上海');
sleep(2)
Select(driver.find_element_by_css_selector('#selectA')).select_by_visible_text('广州');
driver.quit();
十四、弹出框
1、alert 警告
2、confirm 确认
3、prompt 提示
弹出框如果不关闭会使得后面的操作不生效
处理方法:Selenium中对处理弹出框的操作,有专用的处理方法,并且处理的方法是同一个,即alert\confirm\prompt均使用以下方法
1、获取弹出框对象:
alert=driver.switch_to.alert
2、调用
alert.text #返回alert/confirm/prompt中的文字信息
alert.accept() #接受对话框选选
alert.dismiss() # 取消圣诞框选项
提示:无论以上哪个对话框,都可以使用取消,同意,因为调用的是后台的事件,跟页面显示的按钮数量无关。
注意:
1、driver.switch.to.alert 方法适合以上三种对话框,调用时没有括号。
2、获取文本的方法,调用时没有括号,如alert.text
3、在项目中不是所有的小窗口都是以上三种对话框
实例:
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
url='F:/python/hmtlfile/alert.html';
driver.get(url);
driver.maximize_window();
"""
需求:
1、点击alert按钮
2、关闭警告框
3、输入用户名admin
"""
# driver.find_element_by_css_selector("[value='alert警告框']").click();
#driver.find_element_by_css_selector("[value='confirm确认框']").click();
driver.find_element_by_css_selector("[value='prompt提示框']").click();
sleep(3)
#必须切换到弹出框并将其关闭
alert=driver.switch_to.alert;
print('警告框文本是:',alert.text);
alert.accept();
#alert.dismiss(); #取消
alert.
driver.find_element_by_css_selector('.user').send_keys('admin');
sleep(3);
driver.quit();
十五、滚动条操作
1、在HTML页面中,由于前端技术框架的原因,页面元素为动态显示,元素根据滚动条的下拉而被加载
2、页面注册同意条款,需要滚动条到最底层,才能点击同意
实现方式:
selenium中并没有直接提供操作滚动条的方法,但是可以通过js脚本来达到,例如:
1、设置JavaScript脚本控制滚动条
js='window.scrollTo(0,1000)';
2、然后设用js方法
driver.execute_script(js);
实例:
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
url='F:/python/hmtlfile/alert.html';
driver.get(url);
driver.maximize_window();
"""
需求:
1、打开网页,停留2秒
2、拉到最底下
"""
js='window.scrollTo(0,1000)'; #这个0是左边距,即水平滚动条,1000是上边距,即垂直滚动条,一般设到1000像素都会拉到最底下了,如果页面特别长则需要设得大一点,10000都行
driver.execute_script(js);
sleep(3)
driver.quit();
十六、frame切换、多窗口切换(特别重要,每个项目都要用到)
1、frame切换
frame:HTML页面中的一种框架,主要作用是在当前页面中指定区域显示另一个页面的元素:
形式一:[了解]
形式二:
2、切换方法
1)drivier.switch_to.frame(frame_reference) ---->切换到指定frame的方法
frame_reference: 可以为frame框架的name、id或者定位到frame元素
2)driver.switch_to.default_content(); -->切换回默认页面
在frame中操作其他页面,必须先回到默认页面,才能进行下一步操作
实例:
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
url='F:/python/hmtlfile/iframe.html';
driver.get(url);
driver.maximize_window();
"""
需求:
1、在主页面输入用户名
2、在frame1页面中输入用户名
3、在frame2页面中输入用户名
"""
#在主页面输入用户名
driver.find_element_by_css_selector(".names").send_keys('admin');
sleep(2)
#切换到第一个frame并输入用户名
driver.switch_to.frame('frame1'); #这个参数可以是name\id,也可以是iframe元素,例如:
#driver.switch_to.frame(driver.find_element_by_css_selector("[name='frame2']"));
driver.find_element_by_css_selector('.user').send_keys('我是frame1');
sleep(2)
#需要返回主页面才能切换到另一个frame
driver.switch_to.default_content();
#切换到第二个frame
driver.switch_to.frame('frame2');
driver.find_element_by_css_selector('#username').send_keys('我是frame2');
sleep(3)
driver.quit();
2、多窗口切换
在Selenium中封装了获取当前窗口句柄、获取所有窗口句柄和切换到指定句柄窗口的方法:
句柄:handle,窗口的唯一识别码
方法:
1)driver.current_window_handle --->获取当前窗口句柄
2)driver.window_handles --->获取 所有窗口句柄
3)driver.switch_to.window(handle) --->切换指定句柄窗口
步骤:
1、获取当前窗口句柄
2、点击连接,启动另一个窗口
3、获取当前所有窗口句
4、遍历所有窗口句柄
5、判断当前遍历的窗口句柄不等于当前窗口句柄
6、切换
7、返回主窗口
8、再次进入别的窗口
实例
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
url='F:/python/hmtlfile/windows.html';
driver.get(url);
driver.maximize_window();
"""
需求:
1、在主页面输入用户名
2、在新窗口页面1中输入用户名
3、在新窗口页面2中输入用户名
"""
#获取当前窗口句柄 --->目的:判断只要不是当前主窗口句柄,就一定是新开的窗口句柄
curren_handle=driver.current_window_handle;
#在主页面输入用户名
driver.find_element_by_css_selector(".names").send_keys('admin');
sleep(2)
driver.find_element_by_partial_link_text('弹窗页面').click();
#获取所有窗口句柄 这句话的位置不能变
handles=driver.window_handles;
#判断 不是 当前窗口句柄
for h in handles: # h是随便起的变量,表示句柄,该句柄如果包含在所有句柄(handles)中
if h !=curren_handle: #如果不是当前句柄,则:
#切换
driver.switch_to.window(h);
#切换到第二个窗口并输入用户名
driver.find_element_by_css_selector('.user').send_keys('我是frame1');
sleep(3)
#driver.close();
driver.switch_to.window(curren_handle);
driver.find_element_by_css_selector(".names").send_keys('admin1');
driver.find_element_by_partial_link_text('登录注册页面').click();
handles=driver.window_handles;
for h in handles: # h是随便起的变量,表示句柄,该句柄如果包含在所有句柄(handles)中
if h !=curren_handle: #如果不是当前句柄,则:
#切换
driver.switch_to.window(h);
driver.find_element_by_css_selector('#username').send_keys('我是第三个窗口的')
sleep(3)
driver.quit();
切换窗口的方法函数封装起来,要用的时候调用的版本:
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
url='F:/python/hmtlfile/windows.html';
driver.get(url);
driver.maximize_window();
"""
需求:
1、在主页面输入用户名
2、在新窗口页面1中输入用户名
3、在新窗口页面2中输入用户名
"""
#获取当前窗口句柄 --->目的:判断只要不是当前主窗口句柄,就一定是新开的窗口句柄
curren_handle = driver.current_window_handle;
def handless():
handles = driver.window_handles; #获取所有窗口句柄 这句话的位置不能变
# 判断 不是 当前窗口句柄
for h in handles: # h是随便起的变量,表示句柄,该句柄如果包含在所有句柄(handles)中
if h != curren_handle: # 如果不是当前句柄,则:
# 切换
driver.switch_to.window(h);
#在主页面输入用户名
driver.find_element_by_css_selector(".names").send_keys('admin');
sleep(2)
driver.find_element_by_partial_link_text('弹窗页面').click();
handless();
#切换到第二个窗口并输入用户名
driver.find_element_by_css_selector('.user').send_keys('我是frame1');
sleep(3)
#切回主窗口
driver.switch_to.window(curren_handle);
driver.find_element_by_css_selector(".names").send_keys('admin1');
driver.find_element_by_partial_link_text('登录注册页面').click();
#再次调用函数
handless();
driver.find_element_by_css_selector('#username').send_keys('我是第三个窗口的')
sleep(3)
driver.quit();
十七、 截屏
如果自动化测试脚本运行时出现了异常,选择截屏保存当时的信息
因为自动化脚本是由程序去执行的,因此有时候打印的错误信息并不十分明确,如果在执行出错的时候
对当前窗口截图保存,那么通过图片就可以非常直观地看到出错的原因
方法:
driver.get_screenshot_as_file(imgpath)
imgpath:图片保存路径
实例:
import time;
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
url='F:/python/hmtlfile/windows.html';
driver.get(url);
driver.maximize_window();
"""
需求:
1、在页面输入用户名
2、截屏保存在当前路径下(扩展:保存到上一级的image目录下、使用时间戳来动态保存图片)
"""
driver.find_element_by_css_selector('.names').send_keys('admin');
#driver.get_screenshot_as_file('./jiepin.png');
#driver.get_screenshot_as_file('../image/jiepin.png');
driver.get_screenshot_as_file('../image/%s.png'%(time.strftime('%Y_%m_%d %H_%M_%S')));#想要使用time,要先导入time包。ctrl+alt+空格导包。
#strftime是将时间转为字符串
sleep(3);
driver.quit();
十八、验证码处理
验证码是一种随机生成的信息(数字、字母、汉字、图片、算术题等)为了防止恶意的请求行为,增加应用的安全性
处理方式:Selenium中并没有对验证码处理的方法,只能根据项目情况使用以下方式
1、去掉验证码(仅能够在测试环境下使用)
2、设置万能验证码(生产环境和测试环境使用):例如在代码中加入生成的验证码后面加一句“or 888888"之类的,设置一下复杂一点的验证码并且在脚本中提交的一直是这个验证码
3、验证码识别技术(通过python-tesseract来识别图片类型验证码,但成功率很低)
4、记录cookie(通过记录cookie进行跳过登录 :这是最靠谱的一种办法,项目中建议使用这种方法
十九、Cookie
1、说明:
Cookie是由Web服务器生成,并且保存在用户浏览器上的小文本文件,它可以包含用户相关的信息。
数据格式:键值对组成
产生:客户端请求服务器,如果服务器需要记录该用户状态,就向客户端浏览器颁发一个Cookie数据
使用:当浏览器再次请求该网站时,浏览器把请求的数据和Cookie数据一同提交给服务器,服务器检查 该
Cookie,以此来辨认用户状态
2、应用场景:
3、Selenium操作cookie
1)get_cookie(name) --->获取指定cookie 括号里的name是cookie的名称
2)get_cookies() ---->获取本网站所有本地cookies
3)add_cookie(cookie_dict) ---->添加cookie cookie_dict:一个字典对象,必选的键包括:“name" and "value"
3.1 案例:
需求:使用cookie实现跳过登录
1)手动登录百度,获取cookie
2)使用获取到的cookie,达到登录目的,然后就可以执行登录之后的操作
import time;
from selenium import webdriver;
from time import sleep;
driver=webdriver.Chrome();
driver.maximize_window();
url='https://www.baidu.com';
driver.get(url);
driver.add_cookie({'name':'BDUSS','value':'g1a2J2N35RNENxakRQQnpiY3N1WlExNXk3WWtnNFNTcDlLSHNyYXVneTZWTnhlRVFBQUFBJCQAAAAAAAAAAAEAAACwstAKc2h1aXFpbmdmdQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALrHtF66x7ReUW'})
#上面这个value值,是去firefox浏览器复制方便一点,步骤:1、打开百度,点击登录,在输入用户名密码后先清空所有cookie,然后点击【登录】按钮,然后
# 在“网络--->域名为www.baidu.com,类型为html的person文件里,找到Cookie名字为BDUSS后面的一长串内容,复制过来就行了
"""
需求:
使用cookie绕过百度登录
步骤:
1、手动登录百度网站
2、手动获取登录后的cookies 'BDUSS'
3、使用selenium内的add_cookie({'name':'BDUSS','value':'根据实际填写'}) #这个BDUSS是百度的,其他网站的不是这样
"""
sleep(3);
#需要刷新才能看到效果
driver.refresh();
sleep(5);
driver.quit();
扩展:
可以先使用这种方法来获取上面那一长串的value
cookie=driver.get_cookie('BDUSS');
print('cookie:',cookie)
另外:获取所有的cookie信息:
cookies=driver.get_cookies()
二十、UnitTest框架
framework:框架、为解决一类事情的功能集合
UnitTest框架:是python自带的一个单元测试框架,用它来做单元测试。
为什么使用UnitTest框架:
1、能够组织多个用例去执行
2、提供丰富的断言方法
3、能够生成测试报告
UnitTest核心要素
1、TestCase 测试用例
2、TestSuite 套件(批量用例)
3、TestRunner 运行(以文本形式 text TestRunner)
4、TestLoader 运行
5、Fixture (工厂装置)两个函数,前置、后置必运行函数
1、TestCase 测试用例详解:
案例:定义一个实现加法操作的函数,并对该函数进行测试
定义测试用例:
1)、导包:import unittest
2)、定义测试类:新建测试类必须继承unittest.TestCase
3)、定义测试方法:测试方法名称必须以test开头
执行测试用例:
方式一:使用pycharm在代码上点击鼠标右键,选择使用UnitTest运行
方式二:调用unittest.main()来运行
实例:
import unittest;
"""
需求:
定义一个实现加法\减法操作的函数,并对该函数进行测试
步骤:
1)、导包:import unittest
2)、定义测试类:新建测试类必须继承unittest.TestCase
3)、定义测试方法:测试方法名称必须以test开头
"""
#编写求和函数
def add(x,y):
return x+y;
#编写减法函数
def sub(x,y):
return x-y;
#定义测试类并继承
class Test01(unittest.TestCase):
#定义测试方法 注意:以test字母开头
def test_add(self):
#调用 要用的函数
result=add(1,1)
print('结果为:',result);
#定义第二个测试方法:
def test_sub(self): #假设光标定在这里或上面的test_add方法后面,运行unittest,运行结果 那里是显示Test passed:1。
# 如果想运行多个用例,则需要把光标定在classrg dm
result=sub(10,5)
print('结果为:',result);
运行结果为:
Ran 2 tests in 0.003s
OK
结果为: 2
结果为: 5
2、TestSuite 测试套件:
说明:多条用例集合在一起,就是一个TestSuite
使用:
1、实例化:suite=unittest.TestSuite() #suite为TestSuite实例化的名称
2、添加用例:suite.addTest(ClassName("MethodName")) #添加的用例是另外一个py文件写好的UniteTest文件里的类名和方法名
3、添加扩展:suite.addTest(unittest.makeSuite(ClassName)) #搜索指定ClassName内以test开头的方法并添加到测试套件中
实例:
"""
目标:UnitTest框架 ---TestSuite使用
步骤:
1)、导包:import unittest
2)、实例化获取 TestSuite对象
3)、调用addTest() 方法添加用例到指定套件中
"""
#导包:import unittest
import unittest;
from UnitTest.UnitTest_01 import Test01;
#实例化获取 TestSuite对象
suite=unittest.TestSuite()
#调用addTest 方法添加用例到指定套件中
#写法1:类名(”方法名“)
suite.addTest(Test01("test_add"));
suite.addTest(Test01("test_sub"));
#扩展 写法2 添加测试类中所有test开头的方法
suite.addTest(unittest.makeSuite(Test01));
#执行测试套件
runner=unittest.TextTestRunner();
runner.run(suite);
3、TextTestRunner是用来执行测试用例和测试套件的 执行后会有文本显示通过或没通过
使用:
1、先导包
2、实例化:runner=unittest.TextTestRunner();
3、执行:runner.run(suite) #suite为测试套件名称
4、TestLoader 类
说明:用来加载TestCase到TestSuite中,即加载满足条件的油荒用例(默认是以test开头的方法,这里可以按实际情况设置以什么开头),并
把测试用例封装成测试套件。
使用unittest.TestLoader,通过该类下面的discover()方法自动搜索指定目录下指定开头的.py文件,
并将查找到的测试用例组装到测试套件;
用法:
suite=unittest.TestLoader().discover(test_dir,pattern='test*.py')
test_dir :指定的测试用例目录
pattern:为查找的.py文件的格式,默认为'test*.py'
也可以使用unittest.defaultTestLoader 代替 unittest.TestLoader();
运行:
实例:
假设在UnitTest文件夹写UnitTest_Run_testloader.py文件来运行case文件夹下面的用例们,执行其中所有以test开头的.py文件里的用例
"""
目标:使用TestLoader()类
作用:搜索指定目录下指定开头的.py文件,在py文件中搜索test开头的测试方法,并将这些方法添加到测试套件中
需求:运行case目录下test01.py至test05.py文件
步骤:
1)、导包:import unittest
2)、调用方法
3)、执行 套件
"""
#导包:import unittest
import unittest;
#调用方法
#写法一
suite=unittest.TestLoader().discover("../case",pattern='test*.py');#如果不需要指定文件以什么开头,执行case目录下所有模块,则不需要pattern参数
#写法二、扩展,使用defaultTestLoader,不需要括号,推荐使用:
#suite=unittest.defaultTestLoader.discover('../case'); #直接调用case目录下所有模块文件
#执行测试套件
unittest.TextTestRunner().run(suite);
以上实例,假设case目录下的文件列表是这样子的:
test01.py
test02.py
test03.py
test04.py
test05.py
aaaaa.py
则根据pattern='test*.py'来筛选,以a开头的最后一个文件是不会被添加到测试套件中的,仅执行前5个文件,如果不加pattern参数,则第六个也执行
TestLoader与TestSuite的区别(它们都是测试套件,只是实现方式不同)
1、TestSuite需要手动添加测试用例(可以添加测试类,也可以添加测试类中的某个测试方法)
2、TestLoader是搜索指定目录下的指定开头的.py文件,并添加测试类中的所有以test开头的测试方法到测试套件中,不能指定添加测试方法;
工作中建议使用TestLoader
5、Fixture:是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture
控制级别:
1)函数级别(实例1) setUp(self) 每一个方法之前都运行一次,执函数执行之后就会执行一次tearDown()
2)类级别(实例2) setUpClass 只在类之前运行一次,比如执行十个用例,只打开一次浏览器和最大化浏览器和设置隐式等待,如果用函数级别的就会特别烦
注意:类方法必须使用@classmethod修饰
3)模块级别(仅作了解,项目中很少用到):setUpModule()\tearDownModule()
提示:无论使用函数级别还是类级别,最常用的场景就是:
初始化:
1、获取浏览器实例化对象
2、最大化浏览器
3、设置隐式等待
结束:
关闭浏览器驱动
实例1:
"""
目标:unittest 中的fixture用法
fixture其实就是两个函数,这2个函数可以一起使用也可以单独使用
1、初始化函数:def setUp()
2、结束函数:def tearDown()
需求:每个方法之前都要运行一次setUp,之后都要运行一次tearDown
"""
#导包:import unittest
import unittest;
class Test03(unittest.TestCase): #需要继承,写在括号里
def setUp(self):
print("setUp被执行");
def tearDown(self):
print("tearDown被执行")
def test01(self):
print("test01被执行")
def test02(self):
print("test02被执行")
结果是:
setUp被执行
test01被执行
tearDown被执行
setUp被执行
test02被执行
tearDown被执行
实例2、
"""
目标:unittest 中的fixture用法
fixture其实就是两个函数,这2个函数可以一起使用也可以单独使用
1、初始化函数:def setUp()
2、结束函数:def tearDown()
需求:每个方法之前都要运行一次setUp,之后都要运行一次tearDown
"""
#导包:import unittest
import unittest;
class Test03(unittest.TestCase): #需要继承,写在括号里
@classmethod
def setUpClass(cls): #执行类方法,需要使用@classmethod来修饰,否则报错
print("setUpClass被执行");
@classmethod
def tearDownClass(cls):
print("tearDownClass被执行");
def setUp(self):
print("setUp被执行");
def tearDown(self):
print("tearDown被执行")
def test01(self):
print("test01被执行")
def test02(self):
print("test02被执行")
结果是:
setUpClass被执行
setUp被执行
test01被执行
tearDown被执行
setUp被执行
test02被执行
tearDown被执行
tearDownClass被执行
二十一、断言 assert
让程序代替人为判断测试程序执行结果 是否符合预期结果的过程
自动化脚本在执行的时候一般是无人值守状态,我们不知道执行结果是否符合预期结果,所以我们需要让程序
代替人为检测程序执行的结果是否符合预期结果。这就需要使用断言。
UnitTest断言方法:(常用的)
1、assertTrue(expr,msg=None) 验证expr是true,如果是false,则fail
2、assertFalse(expr,msg=None) 验证expr是false,如果是true,则fail
3、assertEqual(expected,actual,msg=None) 验证expected==actual,不等则fail(比较重要,需要掌握)
4、assertNotEqual(first,second,msg=None) 验证first!=second,相等则fail
5、assertIsNone(obj,msg=None) 验证obj是None,不是则fail
6、assertIsNotNone(obj,msg=None) 验证obj不是None,是则fail
7、assertIn(member,container,msg=None) 验证是否member in container(需要掌握)
8、assertNotIn(member,container,msg=None) 验证是否member not in container
实例:
"""
目标:unittest 中的常用断言
1、assertTrue:如果结果为True通过,否则失败
"""
import unittest;
def add(x,y):
return x+y;
class Test03(unittest.TestCase): #需要继承,写在括号里
def test01(self):
result=add(4,4);
flat=result>=8;
self.assertTrue(flat);
self.assertEqual("wq","wq");#应用场景,登录成功后,获取用户名称看是否等于预期的名称
#判断后面的字符串是否包含前面的字符串
self.assertIn('hello','helloword');
#判断是否为None
flag=None;
self.assertIsNone(flag);
实例2:
"""
需求:登录百度
1、输入:输入正确的用户名和不输入密码就点击登录
2、断言:提示信息为“请您输入密码”
3、要求:如果断言出错,截屏保存
"""
import unittest;
from selenium import webdriver;
from time import sleep;
import time;
#定义测试类 并继承 unittest.TestCase
class TestBaiduLogin(unittest.TestCase):
#定义初始化方法
def setUp(self):
#获取浏览器驱动并打开url并最大化浏览器和设置隐式等待
self.driver=webdriver.Chrome();
url="https://baidu.com"
self.driver.get(url);
self.driver.maximize_window();
self.driver.implicitly_wait(30);
#定义结束方法
def tearDown(self):
sleep(3);
self.driver.quit();
#关闭浏览器驱动
#定义登录测试方法 密码输入错误
def test_login_error_psw(self):
driver=self.driver;
# 点击登录连接
driver.find_element_by_css_selector(".s-top-login-btn").click();
driver.find_element_by_css_selector('.tang-pass-footerBarULogin').click();
#输入用户名
driver.find_element_by_css_selector('.pass-text-input-userName').send_keys('shuiqingfu')
#输入密码
driver.find_element_by_css_selector('.pass-text-input-password').send_keys('');
#点击登录
driver.find_element_by_css_selector('.pass-button-submit').click();
sleep(3)
#获取登录后提示信息
result=driver.find_element_by_css_selector(".pass-generalError-error").text;
print('提示信息:',result);
#定义预期结果
expect_result='请您输入密码2';
try:
#断言
self.assertEqual(result,expect_result)
except AssertionError:
# 截图 使用时间戳来动态生成图片名称,不会重复
driver.get_screenshot_as_file("../image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")));
#抛出异常
raise
运行结果:
Tests failef :1
提示信息: 请您输入密码
Ran 1 test in 22.700s
FAILED (errors=1)
Error
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.3.4\plugins\python-ce\helpers\pycharm\teamcity\diff_tools.py", line 32, in _patched_equals
old(self, first, second, msg)
File "C:\Users\wushuirong\AppData\Local\Programs\Python\Python35\lib\unittest\case.py", line 817, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Users\wushuirong\AppData\Local\Programs\Python\Python35\lib\unittest\case.py", line 1190, in assertMultiLineEqual
self.fail(self._formatMessage(msg, standardMsg))
File "C:\Users\wushuirong\AppData\Local\Programs\Python\Python35\lib\unittest\case.py", line 662, in fail
raise self.failureException(msg)
AssertionError: '请您输入密码' != '请您输入密码2'
- 请您输入密码
+ 请您输入密码2
? +
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "F:\python\UnitTest\UnitTest_Run_assert断言实例.py", line 51, in test_login_error_psw
self.assertEqual(result,expect_result)
File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.3.4\plugins\python-ce\helpers\pycharm\teamcity\diff_tools.py", line 38, in _patched_equals
raise error
teamcity.diff_tools.EqualsAssertionError: :: 请您输入密码 != 请您输入密码2
扩展:
"""
目标:python自带的断言
"""
#判断2个字符串是否相等
# assert 'hello'=='hello';
#assert 'hello'=='hallo'; #错误类型:AssertionError
#第二个字符串是否包含第一个字符串
# assert 'h' in 'hello';
# assert 'ha' in 'hello';
#判断是否为True\False 1/0
# assert True;
# assert False;
二十二、参数化
根据需求动态获取参数并引用的过程
应用场景:解决相同业务逻辑,不同测试数据的问题
如何实现:
需要安装unittest扩展插件parameterized来实现
File-->Settings-->Project:python-->Project Interpreter--->点击+号:输入parameterized---->Install Package来进行安装
安装完之后在cmd里面输入pip list可以找到
import unittest
from parameterized import parameterized;
"""
目标:parameter插件应用
步骤:
1、导包 from parameterized import parameterized;
2、修饰测试函数 @parameterized.expand(列表类型数据)
3、在测试函数中使用变量接收,传递过来的值
语法:
1、单个参数:值为列表
2、多个参数:值为列表嵌套元组 如:[(1,2,3),(2,3,4)]
"""
class Test01(unittest.TestCase):
#单个参数
# @parameterized.expand(["1","2","3"])
# def test_add_one(self,num):
# print("num:",num)
#多个参数 写法一:一般调试的时候才这样子写,实际项目中,数据是分离出去的
# data=[(1,2,3),(2,3,5)]
# @parameterized.expand(data)
# def test_add_more(self,a,b,result):
# print('{}+{}={}:'.format(a,b,result))
#多个参数 写法二:实际工作中用这种
#先定义一个获取数据的函数
def get_data():
return [(1,2,3),(3,3,6)]
@parameterized.expand(get_data())
def test_add_more2(self,a,b,result):
print("{}+{}={}:".format(a,b,result));
实例:
import unittest
from parameterized import parameterized;
"""
"""
def add(x,y):
return x+y;
def get_data():
return [(1,2,3),(3,3,5)] #这里的5改成6,则会通过测试,这里3+3=5则会提示出错
class Test01(unittest.TestCase):
@parameterized.expand(get_data())
def test_add_more2(self,a,b,expect):
result=add(a,b);
assert result==expect;
二十三、UnitTest的跳过
对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行
使用方法:
#直接将测试函数标记成跳过
@unittest.skip('代码未完成')
场景:一般适合功能未完成用例
#根据条件判断测试函数是否跳过
@unitest.skipIf(condition,reason)
场景:一般判断条件满足就不执行,如:达到指定版本,此功能失效
实例:
import unittest
version=20; #这个数字如果大于25就会跳过,小于25就会执行test03
class Test01(unittest.TestCase):
@unittest.skip("此方法功能暂未完成")
def test01(self):
"""此方法功能暂未完成"""
def test02(self):
print('test02');
@unittest.skipIf(version>25,"大于25,跳过")
def test03(self):
print("test03");
二十四、html报告
1、首先去下载一个插件
HTMLTestRunner下载
HTMLTestRunner下载地址: http://tungwaiyip.info/software/HTMLTestRunner.html
2、HTMLTestRunner修改
因为这个模块原本给python2.0用的,我用的python是3.x,所以下载后需要做些修改。
下载后修改:(Ctrl+G可以跳转到指定行)
94行引入的名称要改,从 import StringIO 改成import io。
539行 self.outputBuffer = StringIO.StringIO() 要改成self.outputBuffer=io.StringIO()
631行 print >>sys.stderr, ‘\nTime Elapsed: %s’ % (self.stopTime-self.startTime)
修改为:print (sys.stderr, ‘\nTime Elapsed: %s’ %(self.stopTime-self.startTime))
642行,if not rmap.has_key(cls): 需要换成 if not cls in rmap:
766行的uo = o.decode(‘latin-1’),改成 uo=o
772行,把 ue = e.decode(‘latin-1’) 直接改成 ue = e
————————————————
3、HTMLTestRunner存放路径
修改好的模块存放在…\python\tools下
4、把用例放在case文件夹下
5、建一个report文件夹存放报告
6、建立执行代码的文件:
import unittest
import time
from tools.HTMLTestRunner import HTMLTestRunner;
"""
基于unittest框架执行生成hmtl报告
操作:
1、复制HtmlTestRunner.py文件到指定目录
2、导包
3、获取报告存放文件流,并实例化HTMLTestRunner类,执行run方法
"""
#定义测试套件
suite=unittest.defaultTestLoader.discover('../case',pattern='test*.py');
#定义报告存放路径及文件名称
report_dir='../report/{}.html'.format(time.strftime("%Y_%m_%d %H_%M_%S"))
#获取报告文件流 并执行
with open(report_dir,'wb') as f:
HTMLTestRunner(stream=f,verbosity=2,title='XX项目自动化测试报告',description='操作系统 win10').run(suite);
7、运行后在report里就有报告了
二十五、PO实践
PO:page(页面) object(对象)
v1:不采用任何模式(线性模型)
v2:unittest
v3:业务代码和页面对象分离:
v4:实际项目中的PO模式编写:
测试用例(使用TPShop项目的登录页):
1、账号不存在
进入登录页面
输入一个不存在的用户名
输入密码
输入验证码
点击登录按钮
获取错误提示信息
2、密码错误
进入登录页面
输入用户名
输入一个错误的密码
输入验证码
点击登录按钮
获取错误提示信息
V1实例:
#导包
from selenium import webdriver;
import time
#获取driver对象
driver=webdriver.Chrome()
#最大化浏览器
driver.maximize_window();
#隐式等待
driver.implicitly_wait(3);
#打开url
url='http://localhost/index.php/Admin/Admin/login.html'
driver.get(url)
# 输入不存在的用户名
driver.find_element_by_name('username').send_keys('wushuirong');
#输入密码
driver.find_element_by_name('password').send_keys('123456')
#输入验证码
driver.find_element_by_css_selector('#vertify').send_keys('123456')
driver.find_element_by_name('submit').click();
#获取登录后的错误提示信息
tips=driver.find_element_by_css_selector('.error').text;
print('提示信息是:',tips);
expect='用户名不存在';
try:
assert tips==expect;
except AssertionError:
driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
V2实例:
#导包
import unittest;
import time;
from selenium import webdriver;
#新建测试类 并继承
class TestLogin(unittest.TestCase):
driver=None;
#初始化
@classmethod
def setUpClass(cls):
cls.driver=webdriver.Chrome();
cls.driver.maximize_window();
cls.driver.implicitly_wait(3);
url='http://localhost/index.php/Admin/Admin/login.html'
cls.driver.get(url);
#结束
@classmethod
def tearDownClass(cls):
cls.driver.quit();
#新建测试方法
#用户名不存在
def test_login_username_not_exist(self):
# 输入不存在的用户名
driver=self.driver;
driver.find_element_by_name('username').send_keys('wushuirong');
# 输入密码
driver.find_element_by_name('password').send_keys('123456')
# 输入验证码
driver.find_element_by_css_selector('#vertify').send_keys('123456') #因为没有改源代码,所以这个验证码是错的,下面的提示信息永远是验证码错误,暂时先这样
driver.find_element_by_name('submit').click();
# 获取登录后的错误提示信息
tips = driver.find_element_by_css_selector('.error').text;
print('提示信息是:', tips);
expect = '用户名不存在';
try:
assert tips == expect;
except AssertionError:
driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
time.sleep(3);
def test_login_password_error(self):
# 输入正确的用户名
driver = self.driver;
driver.find_element_by_name('username').clear();
driver.find_element_by_name('username').send_keys('admin');
# 输入错误的密码
driver.find_element_by_name('password').send_keys('123456')
# 输入验证码
driver.find_element_by_css_selector('#vertify').send_keys('123456') # 因为没有改源代码,所以这个验证码是错的,下面的提示信息永远是验证码错误,暂时先这样
driver.find_element_by_name('submit').click();
# 获取登录后的错误提示信息
tips = driver.find_element_by_css_selector('.error').text;
print('提示信息是:', tips);
expect = '密码错误';
try:
assert tips == expect;
except AssertionError:
driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
V3实例:页面层和业务层分离
首先在V3文件夹里再建两个文件夹page和scripts分别装页面层和业务层的代码,然后页面层的文件名称必须以page开头,业务层的文件名称以test开头
以下是page_login.py
"""
页面对象编写技巧
类名:使用大驼峰将模块名称抄进来,有下划线就去掉下划线
方法:根据业务需求,每个操作步骤单独封装一个方法
方法名:page_xxx
"""
from selenium import webdriver;
class PageLogin:
def __init__(self):
self.driver = webdriver.Chrome();
self.driver.maximize_window();
self.driver.implicitly_wait(3);
url = 'http://localhost/index.php/Admin/Admin/login.html'
self.driver.get(url);
# 页面层
#输入用户名
def page_input_username(self,username):
self.driver.find_element_by_name('username').send_keys(username);
#输入密码
def page_input_password(self,pwd):
self.driver.find_element_by_name('password').send_keys(pwd);
#输入验证码
def page_input_verify_code(self,code):
self.driver.find_element_by_css_selector('#vertify').send_keys(code)
#点击登录
def page_input_submit(self):
self.driver.find_element_by_name('submit').click();
#获取异常提示信息
def page_input_error(self,error):
return self.driver.find_element_by_css_selector('.error').text;
#业务层
#组装登录业务方法,给业务层调用
def page_login(self,username,pwd,code):
self.page_input_username(username);
self.page_input_password(pwd);
self.page_input_verify_code(code);
self.page_input_submit();
以下是test_login.py:
# 导包
import unittest;
from v3.page.page_login import PageLogin; #把页面层的模块和类导进来
import time;
from parameterized import parameterized;
# 新建测试类
class TestLogin(unittest.TestCase):
# 初始化方法
def setUp(self):
# 获取登录页面对象
self.login = PageLogin();
# 结束方法
def tearDown(self):
self.login.driver.quit();
# 新建测试方法
@parameterized.expand([('wushuirong','123456','8888','账号不存在'),('admin','123455','8888','密码错误')])
def test_login(self, username,pwd,code,expect):
#调用测试登录方法
self.login.page_login(username,pwd,code)
#获取登录后的提示信息
msg=self.login.page_input_error(expect);
#断言
try:
self.assertEqual(msg,expect)
except AssertionError:
self.driver.get_screenshot_as_file('../image/{}.png'.format(time.strftime("%Y_%m_%d %H_%M_%S")));
运行结果是:
Tests failed:2
AssertionError: '验证码错误!' != '账号不存在'
- 验证码错误!
+ 账号不存在
AssertionError: '验证码错误!' != '密码错误'
- 验证码错误!
+ 密码错误
因为没有改过源代码,没有设置万能验证码,所以这里的验证码一直是错的,没办法验证登录名是否存在和密码错误,
实际项目中需要设置万能验证码,或者直接用cookies来跳过登录这一步,或者直接去掉验证码。
V4实例 比V3多了基类,公共的方法适用于任何页面任何项目
建三个文件夹
base (基类): 存放page页面里一些公共的方法,给page用
例如:class Base:
#初始化方法
def __init__(self):
pass
#查找元素方法(暂时提供下面三个方法用)
def base_find_element(self):
pass
#点击方法
def base_click(self):
pass
#输入方法
def base_input(self):
pass
#获取文件方法
def base_get_text(self):
pass
#截图方法
def base_get_image(self):
pass
注意:
1、以上方法,解包只需要一次,在查找元素解包(详细看代码)
2、driver为虚拟,谁调用base时,谁传入,无需关注从哪里来
3、loc:真正使用loc的方法只有查找元素
page(页面对象):一个页面封装成一个对象
应用:继承base;(不用导包的方式)
实现:
1、模块名:page+实际操作模块名称 page_login.py
2、页面对象名:以大驼峰的方法将模块名抄进来,有下划线就去掉下划线
3、方法:涉及元素,将每个元素操作单独封装一个操作方法
4、组装:根据需求组装以上操作步骤。
例如:
class PageLogin():
#输入用户名
def page_input_username(self):
pass
#输入密码
def page_input_password(self):
pass
#输入验证码
def page_input_verity_code(self):
pass
#点击【登录】
def page_click_login_btn(self):
pass
#获取登录异常信息
def page_get_err_info(self):
pass
#截图
def page_get_screenshot(self):
pass
#组合业务方法
def page_login(self):
pass
然后还有一件事情就是页面配置数据,例如登录用户名、密码、验证码等信息,需要另外起一个文件存放,把一个项目里所有类似的信息存放在同一个文件里比较方便管理
就是page文件夹下面建一个__init__.py文件,即利用文件包(文件包比文件夹多一个init文件)的机制,在同一个page文件里的文件可以通过文件夹名.来获取__init__文件下的变量
实际操作看下面的“page文件夹里的详情”:
scripts(业务层):导包调用page页面
实现:
1、模块:test+实际操作模块名称 test_login.py
2、测试业务名称:以大驼峰方法将模块名抄进来,有下划线就去掉下划线。TestLogin
3、方法:
1)初始化方法setUp() 注:在unittest框架中,不能使用def __init__()初始化方法
#实例化页面对象
#前置操作 如:打开登录页
2)结束方法 tearDown()
#关闭浏览器驱动
3)测试方法
#根据要操作的业务来实现
base.py详细代码:
from selenium.webdriver.support.wait import WebDriverWait
import time;
class Base:
#初始化方法
def __init__(self):
# self.driver=driver;
#临时代替driver
self.driver=webdriver.Chrome();
self.driver.maximize_window();
self.driver.get("http://localhost/index.php/Admin/Admin/login.html");
#查找元素方法(暂时提供下面三个方法用)
def base_find_element(self,loc,timeout=30,poll=0.5): #设置后面两个参数的目的是为了有些场景的等待时间可能 会是2秒或其他时长,如果不传参就默认30秒,如果传参就按实际参数来等待
#使用显式等待找到一个元素
return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc)); #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文件夹里的详情:
1、__init__.py的详细代码:
from selenium.webdriver.common.by import By;
"""以下为登录页面元素的配置信息"""
#用户名
login_username=By.NAME,'username';
#密码
login_pwd=By.NAME,'password'
#验证码
login_verify_code=By.CSS_SELECTOR,'#vertify';
#登录按钮
login_btn=By.NAME,'submit';
#获取登录后的错误提示信息
login_err_info=By.CSS_SELECTOR,'.error';
"""以下为订单页面元素的配置信息"""
#暂时没有
page_login.py文件详细代码:
from v4 import page; #在这里包入page包,就可以直接page. 点了之后就会出现刚才在init文件里写的全部变量
from v4.base.base import Base
class PageLogin(Base): #继续Base的方法,在这里输入后按ctrl+alt+空格导包
#输入用户名
def page_input_username(self,username):
self.base_input(page.login_username,username); #这一行代码,首先self.后显示在base里设置的“输入方法 base_input",选择后
#再在括号里输入loc,即页面元素,在init文件里设置的“用户名 login_username",
#这里输入page.之后就会跳出来可以选择该变量;最后的username是参数,需要在外面存
#放的txt等文件里准备好测试数据,再传进来
#输入密码
def page_input_password(self,pwd):
self.base_input(page.login_pwd,pwd)
#输入验证码
def page_input_verity_code(self,code):
self.base_input(page.login_verify_code,code)
#点击【登录】
def page_click_login_btn(self):
self.base_click(page.login_btn);
#获取登录异常信息
def page_get_err_info(self):
return self.base_get_text(page.login_err_info); #获取一定要返回
#截图
def page_get_screenshot(self):
self.base_get_image();
#组合业务方法
def page_login(self,username,pwd,code):
self.page_input_username(username);
self.page_input_password(pwd);
self.page_input_verity_code(code);
self.page_click_login_btn();
scripts文件夹下的test_login.py 详细代码:
#导包
import unittest;
from parameterized import parameterized
from v4.page.page_login import PageLogin;
def get_data():
return [('wushuirong','123456','8888','账号不存在'),('admin','123455','8888','密码错误')];
#新建测试类并继承
class TestLogin(unittest.TestCase):
#setUp
def setUp(self):
#实例化 获取页面对像PageLogin
self.login=PageLogin();
#tearDown
def tearDown(self):
self.login.driver.quit();
#登录测试方法
@parameterized.expand(get_data())
def test_login(self,username,pwd,code,expect_result):
#调用登录方法
self.login.page_login(username,pwd,code);
#获取登录提示信息
msg=self.login.page_get_err_info();
try:
#断言
self.assertEqual(msg,expect_result);
except AssertionError:
self.login.page_get_screenshot();
raise; #加上这句话,会使得如果断言有错就会抛异常,同时截图。如果
#不加上这句话,只会截图,用例还是会pass
PS:断言出错时截图,同时抛异常,用例不是pass:加上raise
最后,在这个方法的类处定点光标,然后运行,即可以进行完整的测试。
二十六、数据驱动(重中之重)
以数据来驱动整个测试用例的执行,也就是测试数据决定测试结果
比如测试加法,测试数据是1和1,那结果就是2,如果测试数据是1和2,测试结果就是3
特点:自动化脚本写完之后基本不会再动,维护的重点在测试数据上,实现它要依赖于参数化的技术
传入数据的方式(测试数据的来源)
1)直接定义在测试脚本中(简单直观,但代码和数据未实现真正的分离,不方便后期维护)
2)从文件读取数据,如JSON、excel、xml、txt等格式文件
3)从数据库中读取数据
4)直接调用接口获取数据源
5)本地封装一些生成数据的方法
1、JSON
是JS对象表示法,基本文本,独立于语言的轻量级数据交换格式
语法规则:
- 大括号保存对象
- 中括号保存数组
- 对象数组可以互相嵌套
- 数据采用键值对表示
- 多个数据由逗号分隔
JSON值
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组
- 对象
- null
数据操作:
1、首先要导入依赖包
import json
2、python字典与JSON之间的转换
a) 把python字典类型转换为JSON字符串
实例:
"""
目标: 把python字典类型转换为JSON字符串
案例:data={"name":"张三","age":18}
操作:
1、导包
2、调用dumps()方法 将字典转换成json字符串
注意:json中还有一个方法dump() 写入json 不要写错
"""
#导包
import json
#定义字典
data={"name":"张三","age":18}
#调用dumps()方法 将字典转换成json字符串
print("转换之前的数据类型:",type(data))
d2=json.dumps(data);
print("转换之后的数据类型:",type(data))
print(d2);
结果是:
转换之前的数据类型:
转换之后的数据类型:
{"name": "\u5f20\u4e09", "age": 18}
b)将json转换成Python字典
"""将字符串转换成字典"""
#定义字符串
data='{"name":"张三","age":18}';
print("转换之前的数据类型:", type(data))
d3=json.loads(data);
print("转换之后的数据类型:", type(d3))
print(data);
结果:
转换之前的数据类型:
转换之后的数据类型:
{"name":"张三","age":18}
注意:
错误写法:data="{'name':'张三'}"
这样子写会报错,说属性名必须用双引号,因此一定要用单引号包双引号来写
3、JSON文件读写
a)读取json文件
with open('data.json',encoding='UTF-8') as f:
data=json.load(f); #返回的数据类型为字典或列表
实例:读取的是下面例子写入的json文件
"""
目标: 读取road
案例:
之前写入的文件
data={"name":"tom","age":18}
"""
#导包
import json
#打开要读取的文件流 并调用load方法
with open("./data/write2.json",encoding="utf-8") as f:
data=json.load(f);
print(data);
b)写入JSON文件
param={'name':'tom', 'age':20}
with open('data2.json', 'w', encoding='UTF-8') as f:
json.dump(param,f); #写什么东西,往哪写 f就是上面那个文件流
"""
目标: 写入json
案例:
1、data={"name":"tom","age":18}
2、data={"name":"张三","age":18}
操作:
1、导包
2、调用dump()方法
"""
#导包
import json
#定义字典
data={"name":"tom","age":18}
data2={"name":"张三","age":18}
#获取文件流 并写入数据
with open("./data/write.json","w",encoding="utf-8") as f:
#调用dump()方法
json.dump(data,f);
with open("./data/write2.json","w",encoding='utf-8') as f2:
json.dump(data2,f2,ensure_ascii=False); #这里如果没有加最后一个ensure_ascii=False,则write2文件里的中文会以阿斯卡玛的方式显示
结果是在data文件夹里生成两个文件write.json和write2.json,内容分别是:
{"age": 18, "name": "tom"}
{"age": 18, "name": "张三"}
综合实例一:网页计算器安全
1、采用po模式 的分层思想对页面进行封装
2、编写测试脚本
3、使用参数化传入测试数据
PS:input的值不是获取text,而是获取其value属性的值
分析:
结构:
base
#初始化方法
#查找元素
#点击
#获取value属性方法封装
#截图
page
#点击数字
for n in str(num):
loc=By.ID,"simple{}".format(n);
#调用base内的方法
#点击加号
#点击等号
#获取结果
#组装业务方法
scripts
#初始化方法
#获取计算页面页面对象
#获取driver
#结果方法
#关闭driver
#调用测试方法
#调用加法运算业务方法
#断言
#截图
driver封装
类名:
#定义类变量
driver=None;
#获取driver方法
@classmethod
def get_driver(cls):
if cls.driver is None:
#实例化浏览器
#最大化
#打开浏览器
cls.driver.get(page.url);
return cls.driver;
#退出driver
@classmethod
def quit_driver(cls):
if cls.driver:
cls.driver.quit();
#注意:此处一定要置空
cls.driver=None;
读取json数据封装
1、读取工具封装
#导包
#打开json获取文件流 并调用load方法
2、组装读取出来的数据格式封装
#预期格式:[(),()]
#默认实际格式{"":"","":""}
思路:
1、新建空列表
2、使用字典.values()方法获取所有的字典值;
3、使用列表.append((字典.get("键名")))
4、返回列表arrs
项目实际:
文件夹与文件层级:
网页计算器实例:
base:
base.py
get_driver.py
data:
calc.json
image:
page:
__init__.py
page_calc.py
scripts:
test_calc.py
tool:
read_json.py
整体思路:
1、首先建好文件夹和文件名后,先写base文件里的基础方法
2、封装好get_driver
3、page文件里先把所有用例涉及的元素写好配置信息,在__init__.py文件里
4、page_calc.py写好全部操作的方法
5、tool里写好read_json.py的读取json文件的方法,以便在写test_calc.py的时候直接调用
6、写test_calc.py文件
7、准备测试用例数据
具体源码:
base.py:
import time;
from selenium.webdriver.support.wait import WebDriverWait
class Base:
#初始化方法
def __init__(self,driver):
self.driver=driver;
#查找元素
def base_find_element(self,loc,timeout=30,poll=0.5):
""" #输入三对双引号后按一下回车键就会显示该方法下的所有参数
:param loc:
:param timeout:
:param poll:
:return:
"""
return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc));
#点击
def base_click(self,loc):
self.base_find_element(loc).click();
#获取value属性方法封装
def base_get_vlaue(self,loc):
return 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;
from 网页计算器实例 import page;
class GetDriver:
#设置类属性
driver=None;
#获取driver
@classmethod
def get_driver(cls):
if cls.driver is None:
#实例化浏览器
cls.driver=webdriver.Chrome();
#最大化
cls.driver.maximize_window();
#打开浏览器
cls.driver.get(page.url);
return cls.driver;
#退出driver
@classmethod
def quit_driver(cls):
if cls.driver:
cls.driver.quit();
#注意:此处有一个大坑,关闭后driver不置空,跑多条用例是跑不起来了,因为上面判断了if cls.driver is None,所以要加下面这句
cls.driver=None;
__init__.py
"""以下为计算器配置数据"""
from selenium.webdriver.common.by import By
#由于数字键有一定的规律,所以暂时不用定位此键,用到的时候再考虑此键怎么解决
# calc_num=By.CSS_SELECTOR,"simple9";
#加号
calc_add=By.CSS_SELECTOR,"#simpleAdd";
#=号
calc_eq=By.CSS_SELECTOR,'#simpleEqual';
#获取结果
calc_result=By.CSS_SELECTOR,'#resultIpt';
#清屏
calc_clear=By.CSS_SELECTOR,'#simpleClearAllBtn';
"""以下为服务器域名配置地址"""
url="http://cal.apple886.com/"
page_calc.py:
from selenium.webdriver.common.by import By
from 网页计算器实例.base.base import Base;
from 网页计算器实例 import page;
class PageCalc(Base):
#点击数字方法
def page_click_num(self,num):
for n in str(num):
loc=By.ID,"simple{}".format(n);
self.base_click(loc);
#点击加号
def page_click_add(self):
self.base_click(page.calc_add)
#点击=号
def page_click_eq(self):
self.base_click(page.calc_eq);
#获取结果方法
def page_get_value(self):
return self.base_get_vlaue(page.calc_result);
#点击清屏
def page_click_clear(self):
self.base_click(page.calc_clear);
#截图
def page_get_image(self):
self.base_get_image();
#组装加法运算方法
def page_add(self,a,b):
self.page_click_num(a);
self.page_click_add();
self.page_click_num(b);
self.page_click_eq();
test_calc.py:
import unittest;
from parameterized import parameterized;
import time;
from 网页计算器实例.base.get_driver import GetDriver
from 网页计算器实例.page.page_calc import PageCalc
from 网页计算器实例.tool.read_json import read_json
def get_data():
datas=read_json('calc.json');
#新建空列表
arrs=[]
for data in datas.values():
arrs.append((data['a'],data['b'],data['expect']));
return arrs;
class TestCalc(unittest.TestCase):
driver=None;
#setUpClass
@classmethod
def setUpClass(cls):
#获取driver
cls.driver=GetDriver().get_driver();
#初始化页面对象
cls.calc=PageCalc(cls.driver);
#tearDown
@classmethod
def tearDownClass(cls):
GetDriver().quit_driver();
#测试方法 加法
@parameterized.expand(get_data())
def test_add_calc(self,a,b,expect):
#调用加法运算业务方法
self.calc.page_add(a,b);
time.sleep(3)
try:
#断言
self.assertEqual(self.calc.page_get_value(),str(expect)); #这里如果不加str会断言出错“3”!=3
#截图
except:
self.calc.page_get_image();
raise;
read_json.py:
#导包
import json;
#调用load方法
def read_json(filename):
filepath='../data/'+filename;
with open(filepath,'r',encoding='utf-8') as f:
return json.load(f);
calc.json:
{
"calc_001":{"a": 1,"b": 2,"expect": 3},
"calc_002":{"a": 1001,"b": 2,"expect":1002},
"calc_003":{"a": 99,"b": 2,"expect": 101}
}
以上测试用例数据,可以看到第二条的预期结果是故意写错的。因此最终运行结果是:
Tests failed:1,passed:2
002 != 1003
Expected :1003
Actual :1002
综合实例二:某网站的登录模块的单元测试实例
1.1 实现步骤
1、绽测试用例
2、采用PO模式的分层思想对页面进行封装
3、编写测试脚本
4、定义数据文件,实现参数化
1.2 用例设计
ID 模块 优先级 测试标题 预置条件 步骤描述 测试数据 预期结果 测试结果
login_001 登录 P0 用户名错误 1、打开首页 1、输入错误用户名 1、用户名:123 提示框提示:
2、点击登录链接 2、输入密码 2、密码:123456 账号不存在
3、输入验证码 3、验证码:万能验证码
4、点击登录按钮
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
login_002 登录 P0 密码错误 1、打开首页 1、输入用户名 1、用户名:username 提示框提示:
2、点击登录链接 2、输入错误密码 2、密码:123456 密码错误
3、输入验证码 3、验证码:万能验证码
4、点击登录按钮
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
login_003 登录 P0 用户名为空 1、打开首页 1、不输入用户名 1、用户名: 提示框提示:
2、点击登录链接 2、输入密码 2、密码:123456 用户名不能为空
3、输入验证码 3、验证码:万能验证码
4、点击登录按钮
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
login_004 登录 P0 密码为空 1、打开首页 1、输入用户名 1、用户名:username 提示框提示:
2、点击登录链接 2、不输入密码 2、密码: 密码不能为空
3、输入验证码 3、验证码:万能验证码
4、点击登录按钮
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
login_005 登录 P0 验证码为空 1、打开首页 1、输入用户名 1、用户名:username 提示框提示:
2、点击登录链接 2、输入密码 2、密码:123456 验证码不能为空
3、不输入验证码 3、验证码:万能验证码
4、点击登录按钮
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
login_006 登录 P0 正常登录 1、打开首页 1、输入用户名 1、用户名:username 登录成功
2、点击登录链接 2、输入密码 2、密码:123456
3、输入验证码 3、验证码:万能验证码
4、点击登录按钮
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
登录成功的标志:一般有"退出"这类的字眼
查找元素判断是否操作成功 思路封装
def base_if_success(self,loc):
try:
self.base_find_element(loc,timeout=2)
#找到元素 assertTrue
return True;
except:
return False;
如何区分正向逆向用例
思路:在测试数据中添加一个标识正向用例或逆向用例的标记,如True/False
步骤:
1、调用登录方法(此登录方法中,只有输入用户名、输入密码、输入验证码、点击登录按钮) 不要封装点击登录连接
2、判断是否正向用例:
判断是否有”退出“
点击”退出“
点击登录连接地址
否则:
获取异常登录 信息
断言操作
如果异常提示是弹框,则需要关闭弹窗
二十八、日志:
用于记录系统运行时的信息,对一个事件的记录;也称为Log
A:作用:
--调试程序
--了解系统程序运行的情况,是否正常
--系统程序运行故障分析与问题定位
--用来做用户行为分析和数据统计
B:日志级别:优先级、重要性或严重程度
DEBUG 调试级别,打印非常详细的日志信息,通常用于对代码的调试
INFO 信息级别,打印一般的日志信息,突出强调程序的运行过程
WARNING 警告级别,打印警告日志信息,表明会出现潜在错误的情形,一般不影响软件的正常使用
ERROR 错误级别,打印错误异常信息,该级别的错误可能会导致系统的一些功能无法正常使用
CRITICAL 严重错误级别,一个严重的错误,这表明系统可能无法继续运行
说明:一般建议只使用前四个级别
当为程序指定一个日志级别后,程序会记录所有日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的。
C:日志的基本用法:
logging模块:python中有一个标准库模块logging可以直接记录日志
C1、步骤:
1、导包 如:import logging
2、调用相应的级别方法,记录日志信息 logging.debug('debug...');
C2、设置级别:
logging.basicConfig(level=logging.DEBUG)
提示:
1、默认级别为:logging.WARNING
2、设置级别时调用的是logging文件夹下面的常量(DEBUG),而不是调用小写的方法
3、切记:设置级别后,日志信息只会记录大于等于此级别的信息;
C3、设置日志格式:
默认的日志格式为:
日志级别:Logger名称:日志内容
自定义日志格式:
logging.basicConfig(format="%(levelname)s:%(name)s:%(message)s");
format参数可能用到的:
占位符 描述
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别 ***
%(pathname)s 调用日志输出函数模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名 ***
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出的语句所在的代码行 ***
%(created)f 当前时间,用UNIX标准的表示 时间的浮点数表示
%(relativeCreated)d 输出日志信息时,自Logger创建以来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是“2003-07-07 16:49:45,897” ****
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 线程ID。可能没有
%(message)s 用户输出的消息 ***
#设置日志格式的示例代码
fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
logging.basicConfig(level=logging.INFO,format=fmt);
C4、将日志信息输出到文件中:
默认情况下Python的logging模块将日志打印到了标准输出中(控制台)
将日志信息输出到文件中:
logging.basicConfig(filename='a.log');
最终代码:
#导包
import logging;
#设置日志格式
fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
#设置日志级别\格式\设置日志保存到指定文件中
logging.basicConfig(level=logging.INFO,format=fmt,filename='./logs/log01.log'); #要用大写的INFO,大写的是变量,小写的是方法
#调用指定级别,输入日志信息 默认只显示warnning及以上的级别。在加入上面那句设置级别之后才会显示全部级别
logging.debug('this is a debug....')
logging.info('info....')
logging.warning("warning");
logging.error('error');
logging.critical('critical');
在logs文件夹里成生的log01.log文件内容是这样子的:
2020-05-21 14:39:36,731 INFO [root] [test01_logging01.py(:12)] - info....
2020-05-21 14:39:36,731 WARNING [root] [test01_logging01.py(:13)] - warning
2020-05-21 14:39:36,731 ERROR [root] [test01_logging01.py(:14)] - error
2020-05-21 14:39:36,731 CRITICAL [root] [test01_logging01.py(:15)] - critical
PS:如果想要把message里的内容写成中文而不显示乱码:
在logging.basicConfic这里按住basicConfic的ctr键进入其底层代码__init__文件里改一下底层代码,但是暂时不要这样子。
日志的使用实例:
step1、先在tool文件夹里加上get_log.py文件,源代码如下:
#导包
import logging;
#定义和获取Logging函数
def get_logging():
fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s';
logging.basicConfig(level=logging.INFO,filename="../log/log01.log",format=fmt);
return logging;
step2、在base.py文件里实例化get_logging和导包,然后在每一个操作前面或后面加上log.info(),如下面源码所示:
from selenium import webdriver;
from selenium.webdriver.support.wait import WebDriverWait
import time;
from testwo.tool.get_log import get_logging
log=get_logging();
class Base:
#初始化方法
def __init__(self, driver):
log.info("初始化driver{}".format(driver));
self.driver = driver;
def base_find_element(self,loc,timeout=30,poll=0.5): #设置后面两个参数的目的是为了有些场景的等待时间可能 会是2秒或其他时长,如果不传参就默认30秒,如果传参就按实际参数来等待
log.info('正在查找元素:{}'.format(loc));
#使用显式等待找到一个元素
return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x:x.find_element(*loc)); #loc是位置
#点击方法
def base_click(self,loc):
log.info('正在点击元素:{}'.format(loc));
self.base_find_element(loc).click();
#输入方法
def base_input(self,loc,value):
log.info('正在给元素{}输入内容:{}'.format(loc,value));
el=self.base_find_element(loc);
el.clear();
log.info('正在清空{}的值'.format(loc));
el.send_keys(value);
log.info('正在给元素{}输入内容'.format(loc));
#获取文件方法
def base_get_text(self,loc):
log.info('正在获取元素:{}的文本'.format(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")));
# 查找元素判断是否操作成功
# 思路封装
def base_if_success(self,loc):
try:
self.base_find_element(loc,timeout=2);
log.info("判断元素:{} 存在".format(loc));
return True;
except:
log.info("判断元素:{} 不存在".format(loc))
return False;
step3、在test_login.py文件里导包和实例化,并在相应的地方加上log.error:
#导包
import unittest;
from parameterized import parameterized
from testwo.base.get_driver import GetDriver
from testwo.page.page_login import PageLogin
from testwo.tool.read_json import read_json
from testwo.tool.get_log import get_logging
log=get_logging();
def get_data():
datas=read_json('login.json');
#新建空列表
arrs=[]
for data in datas.values():
arrs.append((data['username'],data['pwd'],data['code'],data['expect'],data['success']));
return arrs;
class LestLogin(unittest.TestCase):
login=None;
@classmethod
def setUpClass(cls):
try:
# 获取driver
cls.driver = GetDriver().get_driver();
# 初始化页面对象
cls.login=PageLogin(cls.driver);
cls.login.page_click_login_link();
except Exception as e:
log.error(e);
@classmethod
def tearDownClass(cls):
cls.login.driver.quit();
#登录测试方法
@parameterized.expand(get_data())
def test_login(self,username,pwd,code,expect_result,success):
#调用登录方法
self.login.page_login(username,pwd,code);
if success:
#判断“退出”是否存在
try:
self.assertTrue(self.login.page_login_success());
#点击退出
self.login.page_click_logout();
try:
self.assertTrue(self.login.page_logout_success());
except:
self.login.page_get_screenshot();
# 点击登录连接
self.login.page_click_login_link();
except Exception as e:
self.login.page_get_screenshot();
log.error(e);
else:
#获取登录后提示信息
msg=self.login.page_get_err_info();
print('错误信息是:',msg)
try:
self.assertEqual(msg,expect_result);
except Exception as e:
self.login.page_get_screenshot();
log.error(e);
# except AssertionError:
# self.login.page_get_screenshot();
raise;
运行结果生成的log01.log文件如下所示(部分):
在log01.log文件那里右键---show in exploer,然后再用记事本打开:(可以ctr+F查找ERROR关键字来看错误信息)
2020-05-21 16:46:54,709 INFO [root] [base.py(__init__:9)] - 初始化driver
2020-05-21 16:46:54,752 INFO [root] [base.py(base_click:17)] - 正在点击元素:('css selector', '.pull-right>a')
2020-05-21 16:46:54,775 INFO [root] [base.py(base_find_element:12)] - 正在查找元素:('css selector', '.pull-right>a')
2020-05-21 16:46:55,600 INFO [root] [base.py(base_input:21)] - 正在给元素('id', 'inputName')输入内容:shuiqingfu
2020-05-21 16:46:55,601 INFO [root] [base.py(base_find_element:12)] - 正在查找元素:('id', 'inputName')
2020-05-21 16:46:55,672 INFO [root] [base.py(base_input:24)] - 正在清空('id', 'inputName')的值
2020-05-21 16:46:55,727 INFO [root] [base.py(base_input:26)] - 正在给元素('id', 'inputName')输入内容
2020-05-21 16:46:55,731 INFO [root] [base.py(base_input:21)] - 正在给元素('name', 'password')输入内容:
2020-05-21 17:14:43,055 INFO [root] [base.py(base_find_element:12)] - 正在查找元素:('id', 'user.errors')
2020-05-21 17:14:43,566 ERROR [root] [test_login.py(test_login:59)] - :: 验证码错误 != 账号不存在
使用日志的高级用法:
日志封装:
#定义获取日志类
#定义类属性 logger=None
@classmethod
#定义获取logger日志器的类方法
if cls.logger is None: #判断类属性logger是否为空
#获取日志器对象
#设置日志器级别
#获取 控制台处理器
#获取文件处理器
#获取格式器
#将格式器添加到处理器中
#将格式器添加到日志器中
return 类属性 logger
注意:1、以上条件无论是否成立,最后都会返回类属性logger
2、当第一次调用时,条件一定成立,将类属性logger设置不为空
3、当第二次以上调用时,永远返回第一次设置的类属性对象。
具体源码:
#导包
import logging.handlers;
class GetLogger:
logger=None;
@classmethod
def get_logger(cls):
if cls.logger is None:
#获取日志器
cls.logger=logging.getLogger();
#设置日志器 级别
cls.logger.setLevel(logging.INFO);
#获取处理器 控制台
sh=logging.StreamHandler();
#获取处理器 文件--以时间分隔
th=logging.handlers.TimedRotatingFileHandler(filename="../log/log02.log",when='midnight',interval=1,backupCount=30,encoding='utf-8');
#设置格式器
fmt='%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s';
fm = logging.Formatter(fmt);
#将格式器添加到处理器 控制台
sh.setFormatter(fm);
#将格式器添加到 处理器 文件
th.setFormatter(fm);
#将处理器添加到 日志器
cls.logger.addHandler(sh);
cls.logger.addHandler(th);
return cls.logger;
#测试效果,包括中文
if __name__=='__main__':
logger=GetLogger().get_logger();
logger.info("info信息被执行")
logger.error("error信息被执行");
然后在base.py和test.login.py文件里,将:
log=get_logger()
改成:
log=GetLogger().get_logger();
并且重新导包:
from testwo.tool.logger import GetLogger;
之后运行test_login.py生成的日志就是(部分,可以正常显示中文):
2020-05-21 20:57:15,428 INFO [root] [base.py(__init__:12)] - 初始化driver
2020-05-21 20:57:15,429 INFO [root] [base.py(base_click:20)] - 正在点击元素:('css selector', '.pull-right>a')
2020-05-21 20:57:15,429 INFO [root] [base.py(base_find_element:15)] - 正在查找元素:('css selector', '.pull-right>a')
最后:项目实战了
一、自动化测试的流程
1、需求分析
2、挑选适合做自动化测试的功能
3、设计测试用例
4、搭建自动化测试环境【可选】
5、设计自动化测试项目的架构【可选】
6、编写代码
7、执行测试用例
8、生成测试报告并分析结果
实际工作中的流程:
1、将功能用例转化自动化用例(在功能用例模板后新增一列是否自动化)
2、搭建自动化测试环境(本机执行自动化的依赖环境:python\pycharm\浏览器、浏览器驱动、selenium\parameterized)
3、搭建自动化框架(PO模式+数据驱动+log+报告) ps:unittest是一个用例管理和执行的框架
4、编写代码
5、执行用例
6、生成报告\分析log
项目需求:
登录商城、搜索商品并添加到购物车、下订单、支付整个流程
用例使用正向用例
自动化测试结构:
1、base
2、page
3、scripts
4、tool
5、data
6、log
7、image
8、report
根据用例决定base.py文件里有哪些方法:
from selenium import webdriver;
from selenium.webdriver.support.wait import WebDriverWait
class Base:
def __init__(self,driver):
self.driver=driver;
#查找元素 方法 封闭
def base_find_element(self,loc,timeout=30,poll=0.5):
#使用显式等待查找元素
return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).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_screenshot(self):
self.driver.get_screenshot_as_file("../image/{}.png".format("%Y_%m_%d %H_%M_%S"));
#判断元素是否存在的方法
def base_element_is_exist(self,loc):
try:
self.base_find_element(loc,timeout=2);
return True;
except:
return False;
后面的代码,打算把整个实例一起拿出来另外写一篇,这部份的笔记就先这样子了。