Pythone_Selenium实战
自动化测试基础
什么样的项目适合自动化测试
1,任务测试明确,不会频繁变动
2,每日构建后的测试验证;
3,比较频繁的回归测试;
4,软件系统界面稳定,变动少
5,需要在多平台运行的相同案例、组合遍历型的测试、大量重复任务;
6,软件维护周期长;
7,项目进度压力不大;
8,被测软件系统开发比较规范,能够保证系统的可测试性;
9,具备大量的自动化测试平台
10,测试人员具备较强的编程能力
以上具备 3个条件,既可以开展;
什么是 Selenium?
Selenium 自动化测试工具,它主要是用于 Web 应用程序的自动化测试,但并不只局限于此,它还支持所有基于 web 的管理任务自动化。Selenium 的特点:l开源,免费l多浏览器支持:FireFox、Chrome、IE、Operal多平台支持:linux 、windows、MACl多语言支持:java、Python、Ruby、php、C#、JavaScriptl对 web 页面有良好的支持l简单(API 简单)、灵活(用开发语言驱动)l支持分布式测试用例执行
脚本编辑环境搭建
Phthon安装
https://www.python.org/downloads/windows/
环境变量配置
步骤:a: 右键 我的电脑�>属性->高级系统设置->环境变量配置
步骤: b: 在系统变量中点击 新建,输入如下内容(注意变量名可以随便取,变量值就是python的安装目录)
步骤:c: 在系统变量中找到 Path->选择编辑->新建,输入%PYTHON_HOME%(PYTHON_HOME就是上一步中的变量名),接着在新建再输入如下内容:%PYTHON_HOME%\Scripts、%PYTHON_HOME%\DLLs、%PYTHON_HOME%\Lib\lib-tk (注意:后面这几个是为了使用pip,esay_install而添加的,如果不需要则可不用添加了)
浏览器环境搭建
FireFox、 Chrome
安装浏览器驱动driver
1.chromedriver 下载地址:
2.Firefo驱动下载地址:
注意:下载解压后,将chromedriver.exe , geckodriver.exe , Iedriver.exe发到Python的安装目录,例如 D:\python 。 然后再将Python的安装目录添加到系统环境变量的Path下面。
FireFox 需要安装插件 : Firebug、Firepath、Xpath find;
Chrome需要安装插件: x path for free
编写第一个自动化脚本
#coding = utf-8
#导入webdriver驱动from selenium import webdriver#webdriver 的 Firefox 赋值给 browserdriver = webdriver.Firefox()#给浏览器 定义一个地址driver.get('http://www.baidu.com')#查找元素,同时输入selenium2driver.find_element_by_id("kw").send_keys("selenium2")#查找元素 点击提交driver.find_element_by_id("su").click()#关闭driver.quit()
WebDriver API
ID定位 (www.baidu.com)
driver.find_element_by_id("kw")
Name定位(http://www.csdn.net/)
find_element_by_name("passwordtwo")
Class定位
find_element_by_class_name("s_ipt")
Tag定位
find_element_by_tag_name("input")
find_element_by_tag_name("input")
Link定位
link 定位与前面介绍的几种定位方法有所不同,它专门用来定位本链接。百度输入框上面的几个文本
链接的代码如下:<a class="mnav" name="tj_trnews" href="http://news.baidu.com">新闻</a><a class="mnav" name="tj_trhao123" href="http://www.hao123.com">hao123</a><a class="mnav" name="tj_trmap" href="http://map.baidu.com">地图</a><a class="mnav" name="tj_trvideo" href="http://v.baidu.com">视频</a><a class="mnav" name="tj_trtieba" href="http://tieba.baidu.com">贴吧>find_element_by_link_text("新闻")
find_element_by_link_text("hao123")find_element_by_link_text()方法通过元素标签对之间的部分文本信息来定位元素
Partial link定位
parial link 定位是对 link 定们的一个种补充,有些文本连接会比较长,这个时候我们可以取文本链接的
有一部分定位,只要这一部分信息可以唯一的标识这个链接。<a class="mnav" name="tj_lang" href="#">一个很长很长的文本链接</a>通过 partial link 定位如下:find_element_by_partial_link_text("一个很长的")find_element_by_partial_link_text("文本连接")
Xpath定位
//表示当前页面某个目录下,input 表示定位元素的标签名,[@id='kw'] 表示这个元素的 id 属性值等于
kw。下面通过 name 和 class 属性值来定位。find_element_by_xpath("//input[@id='wd']")find_element_by_xpath("//input[@class='s_ipt']")find_element_by_xpath("//*[@class='bg s_btn']")如果不想指定标签名也可以用星号(*)代替。当然,使用 XPath 不仅仅只局限在 id、name 和 class 这三个属性值,元素的任意属性值都可以使用,只要它能唯一的标识一个元素。find_element_by_xpath("//input[@maxlength='100']")find_element_by_xpath("//input[@autocomplete='off']")find_element_by_xpath("//input[@type='submit']")假如百度输入框本身没有可利用的属性值,我们可以查找它的上一级属性。比如,“小明”刚出生的
时候没有名字,没上户口(没身份证号),那么亲朋好友来找“小明”可以先到小明的爸爸,因为他爸爸是有很多属性特征的,找到了小明的爸爸,抱在怀里的一定就是小明了。通过 XPath 描述如下:《Selenium2 Python 自动化测试实战》样张66find_element_by_xpath("//span[@class='bg s_ipt_wr']/input")find_element_by_xpath("//span[@class='bg s_btn_wr']/input")span[@class='bg s_ipt_wr'] 通过 class 属性定位到是父元素,后面/input 也就表示父元素下面标签名为input 的子元素。如果父元素没有可利用的属性值,那么可以继续向上查找“爷爷”元素。find_element_by_xpath("//form[@id='form']/span/input")find_element_by_xpath("//form[@id='form']/span[2]/input")使用逻辑运算符
如果一个属性不能唯一的区分一个元素,我们还可以使用逻辑运算符连接多个属性来区别于其它属性。……<input id="kw" class="su" name="ie"><input id="kw" class="aa" name="ie"><input id="bb" class="su" name="ie">……如上面的三行元素,假如我们现在要定位第一行元素,如果使用 id 将会与第二行元素重名,如果使用class 将会与第三行元素的重名。那么如果同时使用 id 和 class 就会唯一的标识这个元素。那么这个时候就可以通过逻辑运算符号连接。find_element_by_xpath("//input[@id='kw' and @class='su']/span/input")当然,我们也可以用 and 连接更多的属性来唯一的标识一个元素Css定位
"百度搜索框"
<span class="soutu-btn"/>
<input id="kw" class="s_ipt" name="wd" value="" maxlength="255" autocomplete="off"/>
<a id="quickdelete" class="quickdelete" href="javascript:;" title="清空" style="top: 0px; right: 0px; display: none;"/
“百度提交按钮”
<span class="bg s_btn_wr">
<input id="su" class="bg s_btn btnhover" value="百度一下" type="submit"/>
</span>
通过class 属性定位:
find_element_by_css_selector(".s_ipt")
find_element_by_css_selector(".bg s btn btnhover")
通过id 属性定位:
find_element_by_css_selector("#kw")
find_element_by_css_selector("#su")
通过标签名定位:
find_element_by_css_selector("input")
通过父子关系定位:
find_element_by_css_selector("span>input")
上面的写法表示有父亲元素,它的标签名叫 span,查找它的所有标签名叫 input 的子元素
通过属性定位:
find_element_by_css_selector("input[autocomple='off']"
find_element_by_css_selector("input[maxlength='255']")
find_element_by_css_selector("iniput[type='submit']")
在css当中也可以使用元素的任意属性,只要这些熟悉可以唯一的标识这个元素。
组合定位:
find_element_by_css_selector("span.bg s_ipt_wr>input.s_ipt")
find_element_by_cess_selector("span.bg S_ipt_wr>input#su")
用By定位元素
from selenium.webdriver.common.by import By
具体通过哪种定位方式(id 或 name)根据实际场景而定位,By 就可以设
置定位策略find_elenium(By.ID,"kw")
find_elenium(By.NAME,"wd")
find_elenium(By.CLASS_NAME,"s_ipt")
find_elenium(By.TAG_NAME,"input")
find_elenium(By.LINK_TEXT,u"新闻")
find_elenium(By.PARTIAL_LINK_TEXT,u"新闻")
find_elenium(By.XPATH,"//*[@class='bg s_btn]")
find_elenium(By.CSS_SELECTOR,"span.bg s_btn_wr>input#su")
控制浏览器
#设置浏览器大小
driver.set_window_size(480,800)
#设置浏览器最大化
driver.maximize_window()浏览器的前进、后退
driver.get("http://www.baidu.com")
driver.get("http://news.baidu.com")driver.back()driver.forward()简单元素操作
clear() 清除文本、send_keys(*value) 输入 、click()点击元素;
案例: 登录github
from selenium import webdriver
import timedriver = webdriver.Firefox()driver.get("https://github.com/")driver.find_element_by_xpath(".//*[@id='user[login]']").click()driver.find_element_by_xpath(".//*[@id='user[email]']").send_keys("ppppppppppppppppp")paswd=driver.find_element_by_xpath(".//*[@id='user[password]']")paswd.send_keys("0000000000000000000000000000000000000")paswd.clear()time.sleep(100)driver.quit()
注意 :接口提交数据使用submit()要求提交对象是一个表单
driver.find_element_by_xpath("xxxxx").submit()
l size 返回元素的尺寸。
l text 获取元素的文本。user=driver.find_element_by_xpath(".//*[@id='user[login]']").size
email=driver.find_element_by_xpath("html/body/div[4]/div[6]/div[2]/form/p").textprint(user)print(email) l get_attribute(name) 获得属性值。 user=driver.find_element_by_xpath(".//*[@id='user[login]']").get_attribute("type")get_attribute 可以获取 id、class、name、type
l is_displayed() 设置该元素是否用户可见ss#返回元素的结果是否可见,返回结果为 True 或 False
email=driver.find_element_by_xpath("html/body/div[4]/div[6]/div[2]/form/p").is_displayed()
鼠标事件
ActionChains 类提供的鼠标操作的常用方法:
l perform() 执行所有 ActionChains 中存储的行为l context_click() 右击l double_click() 双击l drag_and_drop() 拖动l move_to_element() 鼠标悬停
#定位到要右击的元素
right_click =driver.find_element_by_id("xx")#对定位到的元素执行鼠标右键操作ActionChains(driver).context_click(right_click).perform()
from selenium.webdriver import ActionChains
对于 ActionChains 类下面的方法,在使用之前需要先将模块导入。ActionChains(driver)调用 ActionChains()方法,在使用将浏览器驱动 driver 作为参数传入。context_click(right_click)context_click()方法用于模拟鼠标右键事件,在调用时需要传入右键的元素。perform()执行所有 ActionChains 中存储的行为,可以理解成是对整个操作事件的提交动作
鼠标悬浮操作
#定位到要悬停的元素
above =driver.find_element_by_id("xx")#对定位到的元素执行悬停操作ActionChains(driver).move_to_element(above).perform()
鼠标双击操作
#定位到要悬停的元素
double_click = driver.find_element_by_id("xx")#对定位到的元素执行双击操作ActionChains(driver).double_click(double_click).perform()
鼠标堆放操作(拖动操作)
#定位元素的源位置
element = driver.find_element_by_name("xxx")#定位元素要移动到的目标位置target = driver.find_element_by_name("xxx")#执行元素的拖放操作ActionChains(driver).drag_and_drop(element,target).perform()
键盘事件
from selenium.webdriver.common.keys import Keys
#输入框输入内容
driver.find_element_by_id("kw").send_keys("seleniumm")#删除多输入的一个 mdriver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)#输入空格键+“教程”driver.find_element_by_id("kw").send_keys(Keys.SPACE)driver.find_element_by_id("kw").send_keys(u"教程")#ctrl+a 全选输入框内容driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')#ctrl+x 剪切输入框内容driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')#ctrl+v 粘贴内容到输入框driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'v')#通过回车键盘来代替点击操作driver.find_element_by_id("su").send_keys(Keys.ENTER)
获得验证信息
获得验证处理 常用通过 title 和 URL text 、get_attribute() 获取属性值
#获取页面 title
driver.title#获取页面URLdriver.current_url#获取指定位置的,文本 信息driver.find_element_by_xpath().text
#获取页面 title
title=driver.title#获取页面URLurl=driver.current_url#获取指定位置的,文本 信息baid=driver.find_element_by_xpath(".//*[@id='su']").get_attribute("value")
设置元素等待
显示等待:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECfrom selenium import webdriverfrom selenium.webdriver.common.by import Bydriver=webdriver.Firefox()driver.get("http://www.baidu.com")element=WebDriverWait(driver,5,0,5).until( EC.presence_of_element_located((By.ID,"kw")))element.send_keys("selenium")driver.quit()WebDriverWait()
它是由 webdirver 提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。具体格式如下:WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)driver - WebDriver 的驱动程序(Ie, Firefox,Chrome 等)timeout - 最长超时时间,默认以秒为单位until()
WebDriverWait()一般由 until()(或 until_not())方法配合使用,下面是 until()和 until_not()方法的说明。until(method, message=’ ’)调用该方法提供的驱动程序作为一个参数,直到返回值为 Ture。until_not(method, message=’ ’)调用该方法提供的驱动程序作为一个参数,直到返回值为 False。 Expected Conditions 在本例中, 我们在使用 expected_conditions 类时对其时行了重命名, 通过 as 关键字对其重命名为 EC,并调用 presence_of_element_located()判断元素是否存在。expected_conditions 类提供一些预期条件的实现。title_is 用于判断标题是否 xx。title_contains 用于判断标题是否包含 xx 信息。presence_of_element_located 元素是否存在。visibility_of_element_located 元素是否可见。visibility_of 是否可见presence_of_all_elements_located 判断一组元素的是否存在text_to_be_present_in_element 判断元素是否有 xx 文本信息text_to_be_present_in_element_value 判断元素值是否有 xx 文本信息frame_to_be_available_and_switch_to_it 表单是否可用,并切换到该表单。invisibility_of_element_located 判断元素是否隐藏element_to_be_clickable 判断元素是否点击,它处于可见和启动状态staleness_of 等到一个元素不再是依附于 DOM。element_to_be_selected 被选中的元素。element_located_to_be_selected 一个期望的元素位于被选中。element_selection_state_to_be 一个期望检查如果给定的元素被选中。element_located_selection_state_to_be 期望找到一个元素并检查是否选择状态alert_is_present 预期一个警告信息隐式等待
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECfrom selenium import webdriverfrom selenium.webdriver.common.by import Bydriver=webdriver.Firefox()driver.get("http://www.baidu.com")driver.implicitly_wait(10)
休眠设置
from time import sleep
sleep(2)
定位一组元素
通过 find_elements_xxx
如果遇到 checkbox 点击操作:
1,driver.find_element_by_xx().pop().click()
2, ActionChains(driver).double_click(driver.find_element_by_xx).perform()
如果遇到重复多的 需要进行过滤:
#然后从中过滤出 tpye 为 checkbox 的元素,单击勾选
for i in inputs:if i.get_attribute('type') == 'checkbox':i.click()
多表单切换
from selenium import webdriver
import timeimport osdriver=webdriver.Firefox()file_path='file:///'+ os.path.abspath('fram.html')driver.get(file_path)driver.switch_to.frame("if")driver.find_element_by_id("kw").send_keys("selenium")driver.find_element_by_id("su").click()time.sleep(3)driver.quit()
切换表单:
driver.switch_to.frame("if")
退回到上一步骤:
driver.switch_to.default_content()
如果表单嵌套在当前页面,要先定位表单frame:
切换表单:
driver.switch_to.frame("if")
多窗口切换
current_window_handle 获得当前窗口句柄
window_handles 返回的所有窗口的句柄到当前会话switch_to_window()
from selenium import webdriver
from time import sleepdriver=webdriver.Firefox()driver.implicitly_wait(10)driver.get("http://www.baidu.com")sreach_windows=driver.current_window_handledriver.find_element_by_xpath(".//*[@id='u1']/a[7]").click()sleep(3)driver.find_element_by_xpath(".//*[@id='TANGRAM__PSP_10__submitWrapper']/a[1]").click()sleep(3)all_windos=driver.window_handlesfor handle in all_windos: if handle!=sreach_windows: driver.switch_to.window(handle) print("now register window") driver.find_element_by_xpath(".//*[@id='TANGRAM__PSP_3__userName']").send_keys("username") driver.find_element_by_xpath(".//*[@id='TANGRAM__PSP_3__password']").send_keys("userpassd")for handle in all_windos: if handle == sreach_windows: driver.switch_to.window(handle) print("now sreach winow") #关闭登录tab driver.find_element_by_xpath(".//*[@id='TANGRAM__PSP_4__closeBtn']").click() driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() sleep(3)driver.quit()
警告框处理
switch_to_alert
l text 返回 alert/confirm/prompt 中的文字信息。
l accept 点击确认按钮。l dismiss 点击取消按钮,如果有的话。l send_keys 输入值,这个 alert\confirm 没有对话框就不能用了,不然会报错sleep(2)
move=driver.find_element_by_xpath(".//*[@id='u1']/a[8]")ActionChains(driver).move_to_element(move).perform()sleep(2)driver.find_element_by_xpath(".//*[@id='wrapper']/div[6]/a[1]").click()sleep(2)driver.find_element_by_xpath(".//*[@id='gxszButton']/a[1]").click()sleep(2)driver.switch_to.alert.accept()
上传文件
#coding=utf-8
from selenium import webdriverimport osdriver = webdriver.Firefox()#打开上传功能页面file_path = 'file:///' + os.path.abspath('upfile.html')driver.get(file_path)#定位上传按钮,添加本地文件driver.find_element_by_name("file").send_keys('D:\\upload_file.txt')driver.quit()通过这种方法上传,就绕开了操作 Windows 控件的步骤。如果能找上传的 input 标签,那么基本都可以通过 send_keys()方法向其输入一个文件地址来实现上传
下载文件
让 FireFox 让浏览器能实现文件的载,我们需要通过 FirefoxProfile() 对其参数做一个设置。
browser.download.folderList设置成 0 代表下载到浏览器默认下载路径;设置成 2 则可以保存到指定目录。browser.download.manager.showWhenStarting是否显示开始,Ture 为显示,Flase 为不显示。browser.download.dir用于指定你所下载文件的目录。os.getcwd() 该函数不需要传递参数,用于返回当前的目录。browser.helperApps.neverAsk.saveToDisk指定要下载页面的 Content-type 值,“application/octet-stream”为文件的类型。HTTP Content-type 常用对照表:http://tool.oschina.net/commons这些参数的设置可以通过在 Firefox 浏览器地址栏输入:about:config 进行设置,如图 4.17。图 4.17 FireFire 参数设置
操作 Cookie
业务场景:
开发人员开发一个功能,当用户登录后,会将用户的用户名写入浏览器 cookie,指定的 key 为“username”,那么我们就可以通过 get_cookies() 找到 useranme,
打印 vlaue,如果找不到 username 或对应的 value 为空,那么说明保存浏览器的 cookie 是有问题的#coding=utf-8
from selenium import webdriverfrom time import sleepdriver = webdriver.Firefox()driver.get("http://www.baidu.com")#获取cookie信息cookie = driver.get_cookies()#将获得cookie 的信息打印print(cookie)driver.add_cookie({ 'name':'key-aaaaa','value':'value-bbbbbb'})#遍历cookies的name 和 valuefor cookie in driver.get_cookies(): print("%s -> %s"%(cookie['name'],cookie['value']))driver.quit()l webdriver 操作 cookie 的方法有:
l get_cookies() 获得所有 cookie 信息l get_cookie(name) 返回有特定 name 值有l add_cookie(cookie_dict) 添加 cookie,必须有 nl delete_cookie(name) 删除特定(部分)的 cookl delete_all_cookies() 删除所有 cookie 信息
调用 JavaScript
通过浏览器打开百度进行搜索,搜索的一屏无法完全显示将会出现滚动条。这个时候就可以通过
JavaScript 代码控制滚动条在任意位置,需要改变的就是 scrollTop 的值。通过 execute_script()方法来执行这段 JavaScript 代码
般用到操作滚动条的会两个场景:
l 注册时的法律条文的阅读,判断用户是否阅读完成的标准是:滚动条是否拉到最下方。l 要操作的页面元素不在视觉范围,无法进行操作,需要拖动滚动条。#coding=utf-8
from selenium import webdriverfrom time import sleepdriver = webdriver.Firefox()driver.get("http://www.baidu.com")#搜索driver.find_element_by_id("kw").send_keys("selenium")driver.find_element_by_id("su").click()sleep(3)#将页面滚动条拖到底部js = "var q=document.documentElement.scrollTop=1000"driver.execute_script(js)js_="var q = document.documentElement.scrollTop=0"driver.execute_script(js_)sleep(3)driver.quit()
窗口截图
#coding=utf-8from selenium import webdriverimport logginglogging.basicConfig(level=logging.DEBUG)driver = webdriver.Firefox()driver.get("http://www.baidu.com")try: driver.find_element_by_id("kw_error").send_keys('selenium') driver.find_element_by_id("su").click()except : driver.get_screenshot_as_file("D:\\baidu_error\\baidu_error.jpg") driver.quit()
自动化测试模型
线性测试
模块化与类库
public.py
#coding=utf-8
from selenium import webdriver#登陆def login():driver.find_element_by_id("idInput").clear()driver.find_element_by_id("idInput").send_keys("username")driver.find_element_by_id("pwdInput").clear()driver.find_element_by_id("pwdInput").send_keys("password")driver.find_element_by_id("loginBtn").click()#退出def logout():driver.find_element_by_link_text(u"退出").click()driver.quit()driver = webdriver.Firefox()driver.implicitly_wait(10)driver.get("http://www.126.com")login() #调用登陆模块#收信、写信、删除信件等操作logout() #调用退出模块
mail126.py
#coding=utf-8
from selenium import webdriverfrom time import sleepimport publicdriver = webdriver.Firefox()driver.get("http://www.126.com")#调用登录模块public.login(driver)#调用退出模块public.logout(driver)
数据驱动
#coding=utf-8
from selenium import webdriverfile_info=open('info.txt','r')values = file_info.readlines()file_info.close()for serch in values: driver=webdriver.Firefox() driver.implicitly_wait(10) driver.get("http://www.baidu.com") driver.find_element_by_id("kw").send_keys(serch) driver.find_element_by_id("su").click() driver.quit()
文本读取
读取txt文件
l read() 读取整个文件。
l readline() 读取一行数据。l readlines() 读取所有行的数据#coding=utf-8
zhangsan,123lisi,456wangwu,789from selenium import webdriverfile_info=open('info.txt','r')values = file_info.readlines()file_info.close()for serch in values: username= serch.split(',')[0] passwd=serch.split(',')[1]读取 csv 文件
import csv
#读取本地csv文件my_file='E:\\info.csv'date = csv.reader(open(my_file,'rb'))#循环输出for user in date: print(user)读取xml文件
import xml.dom.minidom as do
#打开xml文档dom=do.parse('info.xml')#获得文档元素对象root=dom.documentElement
获取标签的属性值
#coding=utf-8
import xml.dom.minidom#打开 xml 文档dom = xml.dom.minidom.parse('info.xml')#得到文档元素对象root = dom.documentElementlogins = root.getElementsByTagName('login')#获得 login 标签的 username 属性值username=logins[0].getAttribute("username")print username#获得 login 标签的 passwd 属性值password=logins[0].getAttribute("passwd")print passworditems = root.getElementsByTagName('item')#获得第一个 item 标签有 id 属性值id1=items[0].getAttribute("id")print id1#获得第二个 item 标签有 id 属性值id2=items[1].getAttribute("id")
获取标签之间的数据
#coding=utf-8
import xml.dom.minidom#打开 xml 文档dom = xml.dom.minidom.parse('info.xml')#得到文档元素对象root = dom.documentElementcaptions=dom.getElementsByTagName('caption')#获得第一个标签对的值c1=captions[0].firstChild.dataprint c1#获得第二个标签对的值c2=captions[1].firstChild.dataprint c2#获得第三个标签对的值c3=captions[2].firstChild.dataprint c3
unittest 单元测试框架
提供用例组织与执行:对一个功能编写几个测试用例来说,当然不同讲究用例的组织,但测试用例达
到成百上千时,就需要考虑编写用例的规范与组织,否则后期的维护成本是呈现指数级增加的。可能,我们都无法顾及开发新功能,而花费大量的时间和精力在维护测试用例。这也是单元测试用例的目的。提供比较方法:不是管是功能测试用例,还是单元测试都会有一个预期结果,测试的过程其实就是执行某些操作之后拿实际的结果与预期结果进行比较,当然,这个比较有我们前面介绍来的断言与验证。提供丰富的日志:当所有用例执行完成,要有足够清晰的日志信息告诉我哪些用例失败以及它们失败的位置,如果没这清晰的日志,那么将会大大提供 debug 问题也是一件非常耗时的工作。#coding=utf-8
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import Selectfrom selenium.webdriver.common.keys import Keysfrom selenium.common.exceptions import NoSuchElementExceptionfrom selenium.common.exceptions import NoAlertPresentExceptionimport unittest2,time,reclass BaiduTest(unittest2.TestCase): def setUp(self): self.driver=webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url="http://www.baidu.com" self.verificationErrors=[] self.accept_next_alert= True def test_baidu(self): driver=self.driver driver.get(self.base_url+"/") driver.find_element_by_id("kw").click() driver.find_element_by_id("kw").send_keys("selenium ide") driver.find_element_by_id("su").click() def is_element_present(self,how,what): try: self.driver.find_element(by=how,value=what) except NoSuchElementException as e: return False return True def is_alert_present(self): try: self.driver.switch_to.alert() except NoAlertPresentException as e: return False return True def close_alert_and_get_its_text(self): try: alert=self.driver.switch_to.alert() alert_text=alert.text if self.accept_next_alert: alert.accept() else: alert.dismiss() return alert_text finally: self.accept_next_alert=True def tearDown(self): self.driver.quit() self.assertAlmostEqual([],self.verificationErrors)if __name__ == '__main__': unittest2.main()
unittest 单元测试框架解析
#coding=utf-8
from count import Countimport unittestclass TestCount(unittest.TestCase):def setUp(self):self.j = Count(2,3)def test_add(self):self.add = self.j.add()self.assertEqual(self.add,5)def tearDown(self):passif __name__ == '__main__':unittest.main()unittest 的文档中开篇就介绍了 4 个重要的概念:test fixture, test case, test suite, test
runner,我觉得只有理解了这几个概念,才能真正的理解单元测试的基本原理。test case --测试用例一个 TestCase 的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),实现测试过程的代码(run),以及测试后环境的还原(tearDown)。元测试(unittest)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个功能进行验证。test suite ---测试方案对一个功能的验证往往是需要多测试用例的,可以把多的测试用例集合在一起执行,这就产生了测试套件 TestSuite 的概念,它用来组装单个测试用例,而且 TestSuite 也可以嵌套 TestSuite。可以通过 addTest 加载 TestCase 到 TestSuite 中,再返回一个 TestSuite 实例。test runner ----测试执行TextTestRunner 是来执行测试用例的,其中的 run(test)用来执行 TestSuite/TestCase。测试的结果会保存到 TextTestResult 实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。test fixture -----创建于销毁对一个测试用例环境的搭建和销毁,是一个 fixture,通过覆盖 TestCase 的 setUp()和 tearDown()方法来实现。这个有什么用呢?比如说在这个测试用例中需要访问数据库,那么可以在 setUp()中建立数据库连接以及进行一些初始化, 在 tearDown()中清除在数据库中产生的数据, 然后关闭连接。 注意 tearDown的过程很重要,要为以后的 TestCase 留下一个干净的环境。
# coding=utf-8
import unittest2class Count: def __index__(self, a, b): self.a = a self.b = b def add(self): return self.a + self.bclass TestCount(unittest2.TestCase): def setUp(self): self.j = Count(2, 3) def test_add(self): self.add = self.j.add() self.assertEqual(self.add, 5) def tearDown(self): passif __name__ == '__main__': # 构造suit suit = unittest2.TestSuite() suit.addTest(TestCount("test_add")) # 执行测试 test_suit = unittest2.TextTestRunner() test_suit.run(suit)
count.py
#coding=utf-8
#计算器类class Count: def __init__(self,a,b): self.a = a self.b = b def add(self): return self.a + self.b
all_count.py
#coding=utf-8
from .count import Countimport unittestclass Testcase(unittest.TestCase): def setUp(self): pass #测试整数相加 def test_add(self): self.j=Count(2,3) self.add=self.j.add() self.assertEqual(self.add,5) # 测试小数相加 def test_add2(self): self.j=Count(2.3,4.2) self.add=self.j.add() self.assertEqual(self.add,6.5) #测试字符窜 相加 def test_add3(self): self.j=Count("hello","world") self.add=self.j.add() self.assertEqual(self.add,"helloworld")if __name__ == '__main__': #构造测试集 suit= unittest.TestSuite() suit.addTest(Testcase("test_add")) suit.addTest(Testcase("test_add2")) suit.addTest(Testcase("test_add3")) #执行测试 runner = unittest.TextTestRunner() runner.run(suit)
TestLoader
该类根据各种标准负责加载测试用例,并它们返回给测试套件。正常情况下没有必要创建这个类的实例。unittest 提供了可以共享了 defaultTestLoader 类,可以使用其子类和方法创建实例,所以我们可以使用其下面的 discover()方法来创建一个实例。discover(start_dir,pattern='test*.py',top_level_dir=None)找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到文件名才能被加载。如果启动的不是顶层目录,那么顶层目录必须要单独指定。start_dir :要测试的模块名或测试用例目录。pattern='test*.py' :表示用例文件名的匹配原则。星号“*”表示任意多个字符。top_level_dir=None:测试模块的顶层目录。如果没顶层目录(也就是说测试用例不是放在多级目录中),默认为 None。
#coding =utf-8
import unittestdef creatsuit(): testnuit=unittest.TestSuite() test_dir='E:\\hello' discover=unittest.defaultTestLoader.discover(test_dir,pattern='test*.py',top_level_dir=None ) #discover筛选出来, for test_unit in discover: for test_case in test_unit: testnuit.addTest(test_case) return testnuitif __name__ == '__main__': #测试执行 runner=unittest.TextTestRunner() runner.run(creatsuit())
创建 creatsuite()函数用于查找指定条件的下的所有测试用例,并将其组装到测试套件中。
调用 discover()方法,首先通过 test_dir 定义查找测试文件的目录,pattern 用来定义匹配测试文件的文件名,如果文件名以 test 开头的.py 文件,那么就认为它是一个测试文件。top_level_dir 默认为None。discover()只能找到了测试文件,接下来通过 for 循环来找到测试文件下的每一个测试用例,并且将其添加测试套件中项目实战
测试类型
测试静态内容
静态内容测试是最简单的测试,用于验证静态的、不变化的 UI 元素的存在性。例如:•每个页面都有其预期的页面标题?这可以用来验证链接指向一个预期的页面。•应用程序的主页包含一个应该在页面顶部的图片吗?•网站的每一个页面是否都包含一个页脚区域来显示公司的联系方式,隐私政策,以及商标信息?•每一页的标题文本都使用的<h1>标签吗?每个页面有正确的头部文本内吗?您可能需要或也可能不需要对页面内容进行自动化测试。如果您的网页内容是不易受到影响手工对内容进行测试就足够了。如果,例如您的应用文件的位置被移动,内容测试就非常有价值。测试链接 Web 站点的一个常见错误为的失效的链接或链接指向无效页。链接测试涉及点各个链接和验证预期的页面是否存在。如果静态链接不经常更改,手动测试就足够。但是,如果你的网页设计师经常改变链接,或者文件不时被重定向,链接测试应该实现自动化。功能测试在您的应用程序中,需要测试应用的特定功能,需要一些类型的用户输入,并返回某种类型的结果。通常一个功能测试将涉及多个页面,一个基于表单的输入页面,其中包含若干输入字段、提交“和”取消“操作,以及一个或多个响应页面。用户输入可以通过文本输入域,复选框,下拉列表,或任何其他的浏览器所支持的输入。功能测试通常是需要自动化测试的最复杂的测试类型,但也通常是最重要的。典型的测试是登录,注册网站账户,用户帐户操作,帐户设置变化,复杂的数据检索操作等等。功能测试通常对应着您的应用程序的描述应用特性或设计的使用场景。 测试动态元素 通常一个网页元素都有一个唯一的标识符,用于唯一地定位该网页中的元素。通常情况下,唯一标识符用 HTML 标记的’id’属性或’name’属性来实现。这些标识符可以是一个静态的,即不变的、字符串常量。它们也可以是动态生产值,在每个页面实例上都是变化的。例如,有些 Web 服务器可能在一个页面实例上命名所显示的文件为 doc3861,并在其他页面实例上显示为 doc6148,这取决于用户在检索的‘文档’。验证文件是否存在的测试脚本,可能无法找到不变的识别码来定位该文件。通常情况下,具有变化的标识符的动态元素存在于基于用户操作的结果页面上,然而,显然这取决于 Web 应用程序。 Ajax 的测试 Ajax 是一种支持动态改变用户界面元素的技术。页面元素可以动态更改,但不需要浏览器重新载入页面,如动画,RSS 源,其他实时数据更新等等。Ajax 有不计其数的更新网页上的元素的方法。但是了解 AJAX的最简单的方式,可以这样想,在 Ajax 驱动的应用程序中,数据可以从应用服务器检索,然后显示在页面上,而不需重新加载整个页面。只有一小部分的页面,或者只有元素本身被重新加载
编写自动化测试用例
在编写用例过程中应该遵守以下几点原则:
1、一个脚本是一个完整的场景,从用户登陆操作到用户退出系统关闭浏览器。2、一个脚本脚本只验证一个功能点,不要试图用户登陆系统后把所有的功能都进行验证再退出系统3、尽量只做功能中正向逻辑的验证,不要考虑太多逆向逻辑的验证,逆向逻辑的情况很多(例如手号输错有很多种情况),验证一方面比较复杂,需要编写大量的脚本,另一方面自动化脚本本身比较脆弱,很多非正常的逻辑的验证能力不强。(我们尽量遵循用户正常使用原则编写脚本即可)4、脚本之间不要产生关联性,也就是说编写的每一个脚本都是独立的,不能依赖或影响其他脚本。5、如果对数据进行了修改,需要对数据进行还原。6、在整个脚本中只对验证点进行验证,不要对整个脚本每一步都做验证。
用例存放&维护:
发送邮件:
#coding=utf-8
from selenium import webdriverimport unittest, timefrom public import loginimport xml.dom.minidom#打开 xml 文档dom = xml.dom.minidom.parse('E:\\test_object\\test_date\\login.xml')#得到文档元素对象root = dom.documentElement《Selenium2 Python 自动化测试实战》样张193class TestSendMail(unittest.TestCase):def setUp(self):self.driver = webdriver.Firefox()self.driver.maximize_window()self.driver.implicitly_wait(30)logins = root.getElementsByTagName('url')self.base_url=logins[0].firstChild.dataself.verificationErrors = []#只填写收件人发送邮件def test_send_mail(self):driver = self.driverdriver.get(self.base_url)#登录login.login(self,"testingwtb","a123456")#写信driver.find_element_by_css_selector("#_mail_component_47_47 >span.oz0").click()#填写收件人driver.find_element_by_xpath("//*[@class='bz0']/div[2]/div/input").send_keys('testingwtb@126.com')#发送邮件driver.find_element_by_xpath("/html/body/div[2]/div/div[2]/header/div/div/div/span[2]").click()driver.find_element_by_xpath("//*[@class='nui-msgbox-ft-btns']/div/span").click()#断言发送结果text = driver.find_element_by_class_name('tK1').textself.assertEqual(text,u'发送成功')login.logout(self)def tearDown(self):self.driver.quit()self.assertEqual([], self.verificationErrors)if __name__ == "__main__":unittest.main()
系统会弹框提示“确定真的不需要写主题吗?”,在用例中默认 click
“确定”。邮件发送成功,验证“发送成功”的提示信息
搜索邮件:
............................
#搜索邮件
def test_search_mail(self):driver = self.driverdriver.get(self.base_url)#调用登录模块login.login(self,'testingwtb','a123456')#搜索邮件driver.find_element_by_xpath("//input[@class='nui-ipt-input' and@type='text']").send_keys(u'小明')driver.find_element_by_xpath("//input[@class='nui-ipt-input' and@type='text']").send_keys(Keys.ENTER)#断言搜索邮件标签页面text= driver.find_element_by_xpath("//div[@id='dvMultiTab']/ul/li[5]/div[3]").textself.assertEqual(text,u'搜索邮件')#调用退出login.logout(self).........................................
自动化测试高级应用
安装和使用HTMLTestRunner生成测试报告
HTMLTestRunner 是 Python 标准库的 unittest 单元测试框架的一个扩展。它生成易于使用的 HTML 测
试报告。 HTMLTestRunner 是在 BSD 许可证下发布。首先要下 HTMLTestRunner.py 文件,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.htmlHTMLTestRunner.py 本是一个.py 文件,将它放到 Python 安装目录下即可调用。Windows :将下载的文件放入...\Python\Lib 目录下。#coding=utf-8
from selenium import webdriverimport unittest, timeimport HTMLTestRunner #引入 HTMLTestRunner 包class Baidu(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(10) self.base_url = "http://www.baidu.com/" self.verificationErrors = [] # 百度搜索用例 def test_baidu_search(self): driver = self.driver driver.get(self.base_url) driver.find_element_by_id("kw").send_keys("HTNMLTestRunner") driver.find_element_by_id("su").click() def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors)if __name__ == "__main__":#测试套件 testunit=unittest.TestSuite()#添加测试用例到测试套件中 testunit.addTest(Baidu("test_baidu_search"))#定义个报告存放路径 filename = 'E:\\result.html' fp = open(filename, 'wb')#定义测试报告 runner =HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'百度搜索测试报告',description=u'用例执行情况:')#运行测试用例 runner.run(testunit)#关闭报告文件 fp.close()
更易读的测试报告
……
#百度搜索用例def test_baidu_search(self):u'''百度搜索用例'''driver = self.driverdriver.get(self.base_url)driver.find_element_by_id("kw").send_keys("HTNMLTestRunner")driver.find_element_by_id("su").click()……再次运行测试用例,查看测试报告。如图 9.2
测试报告文件名称
time
time.time() 获取当前时间戳。
time.ctime() 当前时间的字符串形式。time.localtime() 当前时间的 struct_time 形式。time.strftime() 用来获得当前时间,可以将时间格式化为字符串。Python 中时间日期格式化符号(区分大小写): Directive | Meaning |
%a | 星期几的简写 |
%A | 星期几的全称 |
%w | 十进制表示的星期几(值从 0 到 6,星期天为 0) |
%d | 十进制表示的每月的第几天 |
%b | 月份的简写 |
%B | 月份的全称 |
%m | 十进制表示的月份 |
%y | 不带世纪的十进制年份(值从 0 到 99) |
%Y | 带世纪部分的十制年份 |
%H | 24 小时制的小时 |
%I | 12 小时制的小时 |
%p | 本地的 AM 或 PM 的等价显示 |
%M | 十时制表示的分钟数 |
Directive | Meaning |
%S | 十进制的秒数 |
%f | 十进制的微秒,零填充左边 |
%Z | 当前时区的名称 |
%j | 十进制表示的每年的第几天 |
%U | 一年中的星期数(00-53)星期天为星期的开始 |
%W | 一年中的星期数(00-53)星期一为星期的开始 |
%x | 本地相应的日期表示 |
%X | 本地相应的时间表示 |
%% | %号本身 |
#coding=utf-8
import unittestimport HTMLTestRunnerimport timedef creatsuite():testunit=unittest.TestSuite()#定义测试文件查找的目录test_dir='E:\\test_object\\test_case'#定义 discover 方法的参数discover=unittest.defaultTestLoader.discover(test_dir,pattern='test*.py',top_level_dir=None)#discover 方法筛选出来的用例,循环添加到测试套件中for test_case in discover:print test_casetestunit.addTests(test_case)return testunitnow = time.strftime("%Y-%m-%d %H_%M_%S")filename = 'E:\\test_object\\report\\'+now+'result.html'fp = file(filename, 'wb')runner =HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'百度搜索测试报告',description=u'用例执行情况:')alltestnames = creatsuite()
runner.run(alltestnames)fp.close()
Selenium Grid2
Selenium2 工作原理
Selenium2 中因为使用的 WebDriver,这个技术不是靠 js 驱动的,而是直接调用浏览器的原生态接口驱动的。所以就没有同源问题,也就不需要使用 RC 来执行本地脚本了(当然缺点就是并不是所有的浏览器都有提供很好的驱动支持,但 JavaScript 却是所有浏览器都通用的)。所以 Selenium2 中执行本地脚本的方式是:通过本地 WebDriver 驱动直接调用本地浏览器接口就可以了。Selenium 1(RC)代码我们可以通过 Selenium IDE 将录制的脚本导出为“Python2/unittest/Remote Control”格式。通过编辑器打开导出的脚本selenium grid 工作方式
#coding=utf-8
from selenium import seleniumimport unittest, time, reclass serc(unittest.TestCase):def setUp(self):self.verificationErrors = []self.selenium = selenium("localhost", 4444, "*chrome","http://www.baidu.com/")self.selenium.start()def test_serc(self):sel = self.seleniumsel.open("/")sel.type("id=kw", "selenium grid")sel.click("id=su")sel.wait_for_page_to_load("30000")def tearDown(self):self.selenium.stop()self.assertEqual([], self.verificationErrors)if __name__ == "__main__":unittest.main()Selenium Server 环境配置
环境搭建
第一步、 配置 java 环境
java 下载地址:http://www.java.com/zh_CN/download/manual.jsp第二步、下载运行 selenium server
下载地址:http://selenium-release.storage.googleapis.com/2.41/selenium-server-2.41.0.zip在页面的左侧列表中找到 selenium-server-standalone-XXX.jar 进行下载。下载完成可以放到任意位置,直接在命令提示符下启动 Selenium Server:C:\selenium> java -jar selenium-server-standalone-XXX.jar
调用 Selenium Grid 的基本结构图
样例演示 :
在浏览器中打开:http://127.0.0.1:4444/grid/console
Selenium Grid 应用
WebDriver 提供了 Remote 可以发送指令到远程服务器控制浏览器。 首先我们先来认识 Remote 的格式
from selenium.webdriver import Remote
from selenium.webdriver.common.desired_capabilities import DesiredCapabilitiesdriver = Remote(command_executor='http://127.0.0.1:4444/wd/hub',desired_capabilities=DesiredCapabilities.CHROME)driver.get('http://www.baidu.com')……driver.quit()
Desired Capabilities 本质上是 key value 的对象,它告诉 Selenium Server 脚本执行的基本运行环
境:'platform': 'ANY' 平台默认可以是任何(Windows,MAC,android)。'browserName': 'chrome' 浏览器名字是 chrome。'version': '' 浏览器的版本默认为空。'javascriptEnabled': True javascript 启动状态为 True
from selenium.webdriver import Remote
from selenium.webdriver.common.desired_capabilities import DesiredCapabilitiesdriver = Remote(command_executor='http://127.0.0.1:4444/wd/hub',desired_capabilities={'platform': 'ANY','browserName':'chrome','version': '','javascriptEnabled': True})driver.get('http://www.baidu.com')……driver.quit()
WebDriver API 提供了不同平台及浏览器的参数:
ANDROID = {'platform': 'ANDROID', 'browserName': 'android', 'version': '', 'javascriptEnabled': True} |
CHROME = {'platform': 'ANY', 'browserName': 'chrome', 'version': '', 'javascriptEnabled': True} |
FIREFOX = {'platform': 'ANY', 'browserName': 'firefox', 'version': '', 'javascriptEnabled': True} |
HTMLUNIT = {'platform': 'ANY', 'browserName': 'htmlunit', 'version': ''} |
HTMLUNITWITHJS = {'platform': 'ANY', 'browserName': 'htmlunit', 'version': 'firefox', 'javascriptEnabled': True} |
INTERNETEXPLORER = {'platform': 'WINDOWS', 'browserName': 'internet explorer', 'version': '', 'javascriptEnabled': True} |
IPAD = {'platform': 'MAC', 'browserName': 'iPad', 'version': '', 'javascriptEnabled': True} |
IPHONE = {'platform': 'MAC', 'browserName': 'iPhone', 'version': '', 'javascriptEnabled': True} |
SAFARI = {'platform': 'ANY', 'browserName': 'safari', 'version': '', 'javascriptEnabled': True} |
PHANTOMJS = {'platform': 'ANY', 'browserName': 'phantomjs', 'version': '', 'javascriptEnabled': True} |
OPERA = {'platform': 'ANY', 'browserName': 'opera', 'version': '', 'javascriptEnabled': True} |
参数化浏览器执行用例
#coding=utf-8from selenium.webdriver import Remotefrom selenium.webdriver.common.desired_capabilities import DesiredCapabilities#浏览器数组lists=['chrome','firefox','internet explorer']#读取不同的浏览器执行脚本for browser in lists:print browserdriver = Remote(command_executor='http://127.0.0.1:4444/wd/hub',desired_capabilities={'platform': 'ANY','browserName':browser,'version': '','javascriptEnabled': True})driver.get("http://www.baidu.com")driver.find_element_by_id("kw").send_keys(browser)driver.find_element_by_id("su").click()driver.close()
多节点执行用例
#coding=utf-8
from selenium.webdriver import Remotefrom selenium.webdriver.common.desired_capabilities import DesiredCapabilities#定义主机与浏览器lists={'http://127.0.0.1:4444/wd/hub':'chrome','http://127.0.0.1:5555/wd/hub':'firefox','http://127.0.0.1:5556/wd/hub':'internet explorer'}#通过不同的浏览器执行脚本for host,browser in lists.items():print host,browserdriver = Remote(command_executor=host,desired_capabilities={'platform': 'ANY','browserName':browser,'version': '','javascriptEnabled': True})driver.get("http://www.baidu.com")driver.find_element_by_id("kw").send_keys(browser)driver.find_element_by_id("su").click()driver.close()
启动远程 node
我们目前启动的 hub 与 node 都是在一台主机上。 那么要在其它主机启动 node 必须满足以下几个要求:
l 本地 hub 主机与远程 node 主机之间可以相互 ping 通。l 远程主机必须安装运行脚本的运行环境(Python 、Selenium、浏览器及浏览器驱动 )。l 远程主机必须安装 java 环境,因为需要运行 Selenium Server。操作步骤如下:启动本地 hub 主机(本地主机 IP 为:172.16.10.66): C:\selenium>java -jar selenium-server-standalone-2.39.0.jar -role hub |
fnngj@fnngj-VirtualBox:~/selenium$ java -jar selenium-server-standalone-2.39.0ar -role node -port 5555 -hub http://172.16.10.66:4444/grid/register |
小技巧:
自从使用 Selenium Server 执行自动化脚本后,常常会因为忘记启动 Selenium Server 导致脚本运行报错。其实我们以在脚本中执行 cmd 命令。如在我们创建的 all_test.py 文件中添加:import osos.chdir('E:\\selenium')os.system('java -jar selenium-server-standalone-2.39.0.jar')这样就再也不会因为忘记启动 Selenium Server 导致测试脚本运行失败了
https://www.cnblogs.com/yufeihlf/p/5764099.html
http://blog.csdn.net/boonya/article/details/55049483