Selenium自动化测试

什么是自动化测试

自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行
结果。总的概括即:将人为驱动的测试行为转化为机器执行的过程。
进入今天的主角:selenium 学习功能测试自动化首选工具就是selenium,它是一个web自动化测试工具。

selenium的特点

  • 支持多平台:IE、Chrome、Firefox、edge、Safari
  • 支持多语言:Python、C、Java、C#、ruby、js
  • 免费小巧,支持分布式测试用例的执行,可以把测试用例分布到不同的测试机器执行,相当于分发机的功能。

自动化工具和自动化框架的区别

在学习selenium的过程中,我们写测试脚本需要使用unittest框架,这个框架提供的类或函数可以实现我们想要实现的测试功能。自动化测试框架一般可以分为两个层次,上层是管理整个自动化测试的开发,执行以及维护,在比较庞大的项目中,它体现重要的作用,它可以管理整个自动测试,包括自动化测试用例执行的次序、测试脚本的维护、以及集中管理测试用例、测试报告和测试任务等。下层主要是测试脚本的开发,充分的使用相关的测试工具,构建测试驱动,并完成测试业务逻辑。

自动化工具本片博客主要说的是selenium,除此之外,还有QTP、Rational Robot 、jmeter、appium、soapui、Loadrunner等等,以后有时间再去学习一下。工具主要的特点就是每个工具都有自己独特的操作界面供用户使用,selenium中的Selenium IDE就是自动化测试工具。

自动化测试适合适用什么项目,适合在什么时机做这种自动化合适?

自动化测试分为UI自动化测试和接口自动化测试。

UI自动化测试:适用于界面比较稳定的项目,前端开发完成之后,并且项目功能稳定。UI界面测试适合进行回归测试以及兼容性测试。

接口自动化测试:适用于后端开发完成,并且项目功能稳定;后端完成之后,就可以进行接口测试。做接口自动化的工具:soupUI、jmeter。

实施自动化测试的前提条件:需求变动不频繁、项目周期长、自动化测试脚本可以重复使用。

自动化适合的项目:产品型项目、机械并频繁的测试(比如兼容性测试)

自动化测试的优势

降低大型系统的由于变更或者多期开发引起的大量的回归测试的人力投入,这可能是自动化测试最主要的任务,特
别是在程序修改比较频繁时,效果是非常明显的,自动化测试前期人力投入较多,但后期进入维护期后,可节省大
量人力,而手工测试后期需要增加大量人力用于回归测试。

  • 减少重复测试的时间,实现快速回归测试
  • 创建优良可靠的测试过程,减少人为错误
  • 可以运行更多更繁琐的测试
  • 可以执行一些手工测试困难或不可能进行的测试
  • 更好的利用资源
  • 测试具有一致性和重复性
  • 测试脚本的重用性

selenium的实现原理

Selenium主要有三个版本,分别是Selenium1.0,Selenium2.0,Selenium3.0

Selenium1.0:包括selenium IDE、selenium RC、selenium Grid(支持分布式)。Selenium1.0核心是selenium RC,所以Selenium1.0又称为Selenium RC,它是在浏览器中运用JavaScript应用,即用JavaScript代码获取页面上的任何元素并执行各种操作

Selenium2.0:核心是WebDriver,Selenium+WebDriver

WebDriver的原理:

(1)启动web浏览器,后台会同时启动基于Webdriver Wire协议的Web服务器作为selenium的远程服务器,并将其与浏览器绑定。绑定完成之后,服务器就开始监听客户端的操作请求。

(2)执行测试时,测试用例会作为客户端,将需要执行的页面操作请求以HTTP请求的方式发送给远程服务器。该HTTP请求的正文以Webdriver Wire协议规定的JSON格式来描述需要浏览器执行的具体操作。

(3)远程服务器接收到请求后,会对请求进行解析,并将解析结果发送给Webdriver,由Webdriver实际执行浏览器的操作。

(4)Webdriver可以看作是直接操作浏览器的原生组件,所以搭建测试环境时,通常需要先下载浏览器对应的WebDriver。

业界有一个形象的比喻来理解:乘客和出租车的例子。乘客就是客户端编写的测试脚本,司机就是我们的WedDriver,而出租车就是浏览器。

Selenium3.0:增加了edge 、Safari的原生驱动

学习Selenium主要就是学习WebDriver常用的API

常见API详解

 #防止乱码
#coding = utf-8
#想使用selenium的webdriver里面的函数,首先需要把包导进来
from selenium import webdriver
import time
#我们需要操作的浏览器,这里使用的是谷歌浏览器,也可以是IE、Firefox
driver =webdriver.Chrome()
#访问百度首页
driver.get('http://www.baidu.com')#停3秒钟
time.sleep(3)
#百度输入框的id为kw,我们需要在输入框中输入Selenium,用send_keys进行输入
driver.find_element_by_id("kw").send_keys("Selenium")
time.sleep(3)
#百度搜索框按钮id叫su,找到后调用click函数模拟点击操作
#和click有相同效果的是submit(),都可以用来点击按钮,submit主要是用于提交表单
driver.find_element_by_id("su").click()
time.sleep(3)
#退出并关闭窗口的每一个相关的驱动程序
driver.quit()

注意:关闭窗口主要有两种方法,分别是close和quit。close是关闭当前浏览器窗口,quit不仅关闭窗口,还会彻底的退出webdriver,释放driver server之间的连接,所以quit的关闭比close更彻底,它会更好的释放资源。

元素的定位

对页面元素进行定位是自动化测试的核心,我们要想操作一个对象,首先应该识别这个对象。在一个页面中,每个对象属性是唯一的我们需要用一系列的方式去定位我们要操作的对象。WebDriver提供了以下几种方法定位元素:

  • id
  • name
  • class name
  • link text
  • partial link text
  • tag name
  • xpath
  • css selector

下面我将对每种定位方法进行举例。

#coding = utf-8
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
######################百度输入框的定位方式#################
#通过id定位
driver.find_element_by_id("kw").send_keys("selenium")
#通过name定位
driver.find_element_by_name("wd").send_keys(u"CSDN博客") #u表示以utf-8的格式输入
#通过tag name定位
driver.find_element_by_tag_name("input").send_keys("Python") #不能成功,因为input在这个页面有多个不唯一,无法定位到底是哪一个
#通过class name定位
driver.find_element_by_class_name("s_ipt").send_keys("Java")
#通过CSS定位
driver.find_element_by_css_selector("#kw").send_keys("C++")
#通过xpath定位
driver.find_element_by_xpath("//*[@id=kw]").send_keys(u"C语言")
#通过link test定位
driver.find_element_by_link_text("hao123").click()
#通过partial link test定位
driver.find_element_by_partial_link_text("hao").click()
driver.find_element_by_id("su").click()
time.sleep(3)
driver.quit()

智能等待

前面说过等待可以引入time包,从而在脚本中自由的添加休眠时间 。但是有时候我们不想等待一个固定的时间,于是可以通过implicitly_wait()方法方便的实现智能等待,它在一个时间范围内智能等待。

selenium.webdriver.remote.webdriver.implicitly_wait(time_to_wait)隐式等待一个元素被发现或一个命令完成,这个方法每次会话只需要调用一次time_to_wait

具体用法:

# coding = utf-8
from selenium import webdriver
import time #调入time 函数
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
browser.implicitly_wait(30) #智能等待30秒
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
browser.quit()

打印信息

使用print打印title和URL

#coding = utf-8
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('http://www.baidu.com')
print driver.title # 把页面title 打印出来
print driver.current_url #打印url
driver.quit()

浏览器的操作

浏览器的最大化

调用启动的浏览器不是全屏的,这样虽然不影响脚本的执行,但有时会影响我们观看脚本执行的变化。使用maximize_window()

#coding=utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
print "浏览器最大化"
browser.maximize_window() #将浏览器最大化显示
time.sleep(2)
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
time.sleep(3)
browser.quit()

设置浏览器的宽高

最大化不够灵活,所以我们可以随意的设置浏览页面的宽高,使用set_window_size(宽,高)

#coding=utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
time.sleep(2)
#参数数字为像素点
print "设置浏览器宽480、高800显示"
browser.set_window_size(480, 800)
time.sleep(3)
browser.quit()

浏览器的前进、后退

我们也可以实现浏览器的前进和后退

#coding=utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome()
#访问百度首页
first_url= 'http://www.baidu.com'
print "now access %s" %(first_url)
browser.get(first_url)
time.sleep(2)
#访问新闻页面
second_url='http://news.baidu.com'
print "now access %s" %(second_url)
browser.get(second_url)
time.sleep(2)
#返回(后退)到百度首页
print "back to %s "%(first_url)
browser.back()
time.sleep(1)
#前进到新闻页
print "forward to %s"%(second_url)
browser.forward()
time.sleep(2)
browser.quit()

控制浏览器滚动条

#coding=utf-8
from selenium import webdriver
import time
#访问百度
driver=webdriver.Chrome()
driver.get("http://www.baidu.com")
#搜索
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
time.sleep(3)
#将页面滚动条拖到底部
js="var q=document.documentElement.scrollTop=10000"
driver.execute_script(js)
time.sleep(3)
#将滚动条移动到页面的顶部
js="var q=document.documentElement.scrollTop=0"
driver.execute_script(js)
time.sleep(3)
driver.quit()
#excute_script(script,*args),在当前窗口同步执行JavaScript

键盘事件

键盘键用法

#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys #需要引入keys 包
import os,time
driver = webdriver.Chrome()
driver.get("http://demo.zentao.net/user-login-Lw==.html")
time.sleep(3)
driver.maximize_window() # 浏览器全屏显示
driver.find_element_by_id("account").clear()
time.sleep(3)
driver.find_element_by_id("account").send_keys("demo")
time.sleep(3)
#tab 的定位相当于清除了密码框的默认提示信息,等同上面的clear()
driver.find_element_by_id("account").send_keys(Keys.TAB)
time.sleep(3)
#通过定位密码框,enter(回车)来代替登陆按钮
driver.find_element_by_name("password").send_keys(Keys.ENTER)
time.sleep(3)
driver.quit()

键盘组合键用法

实现Ctrl+a,Ctrl+x

#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
#输入框输入内容
driver.find_element_by_id("kw").send_keys("selenium")
time.sleep(3)
#ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
time.sleep(3)
#ctrl+x 剪切输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')
time.sleep(3)
#输入框重新输入内容,搜索
driver.find_element_by_id("kw").send_keys("webdriver")
driver.find_element_by_id("su").click()
time.sleep(3)
driver.quit()

鼠标事件

操作鼠标需要使用到ActionChains类

  • context_click()右击
  • double_click()双击
  • drag_and_drop()拖动
  • move_to_element()移动
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Chrome()
driver.get("http://news.baidu.com")
qqq =driver.find_element_by_xpath(".//*[@id='s_btn_wr']")
ActionChains(driver).context_click(qqq).perform() #右键
ActionChains(driver).double_click(qqq).perform() #双击
#定位元素的原位置
element = driver.find_element_by_id("s_btn_wr")
#定位元素要移动到的目标位置
target = driver.find_element_by_class_name("btn")
#执行元素的移动操作
ActionChains(driver).drag_and_drop(element, target).perform()
#ActionChains(driver)生成用户的行为。所有的行动都存储在actionchains 对象。通过perform()存储的行为。
#move_to_element(menu)移动鼠标到一个元素中,menu 上面已经定义了他所指向的哪一个元素
#perform()执行所有存储的行为

定位一组元素

前面我们定位元素时,只定位了某一个特定的对象,但有时我们需要定位一组对象,这就需要使用find_elements方法

#coding=utf-8
from selenium import webdriver
import time
import os
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\checkbox.html')
dr.get(file_path)
# 选择页面上所有的input,然后从中过滤出所有的checkbox 并勾选之
inputs = dr.find_elements_by_tag_name('input')
for input in inputs:if input.get_attribute('type') == 'checkbox':input.click()
time.sleep(2)
dr.quit()
#get_attribute获取属性值

多层框架/窗口定位

  • switch_to_frame()多层框架定位
  • switch_to_window() 多窗口定位

switch_to_frame()的功能是把当前定位的主体切换到frame里,即frame中实际上嵌入了另一个页面,而webdriver每次只能在一个页面识别,因此才需要用switch_to_frame方法去获取frame中嵌入的页面,对那个页面里的元素进行定位。

switch_to _default_content:从frame中嵌入的页面跳出,跳回到最外面的原始页面中。

#coding=utf-8
from selenium import webdriver
import time
import os
browser = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\frame.html')
browser.get(file_path)
browser.implicitly_wait(30)
#先找到到ifrome1(id = f1)
browser.switch_to_frame("f1")
#再找到其下面的ifrome2(id =f2)
browser.switch_to_frame("f2")
#下面就可以正常的操作元素了
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
time.sleep(3)
browser.quit()

多层窗口定位:switch_to_window用法与switch_to_frame相同,如:driver.switch_to_window("windowname")

层级定位

对于这种定位的思路是:先点击显示出1个下拉菜单,然后再定位该下拉菜单所在的ul,再定位这个ul下的某个具体的link,如果要定位第一个下拉菜单中的Action选项:

#coding=utf-8
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import time
import os
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\level_locate.html')
dr.get(file_path)
#点击Link1链接(弹出下拉列表)
dr.find_element_by_link_text('Link1').click()
#找到id为dropdown1的父元素
WebDriverWait(dr,10).until(lambda the_driver:#10秒内每隔500毫秒扫描1次页面变化,当出现指定的元素结束
the_driver.find_element_by_id('dropdown1').is_displayed())#is_displayed()表示是否用户可见
#在父亲元件下找到link 为Action 的子元素
menu = dr.find_element_by_id('dropdown1').find_element_by_link_text('Action')
#鼠标定位到子元素上
webdriver.ActionChains(dr).move_to_element(menu).perform()
time.sleep(2)
dr.quit()

下拉框处理

对于下拉框里的内容我们需要两次定位,先定位到下拉框,再定位到下拉框内的选项

#coding=utf-8
from selenium import webdriver
import os,time
driver= webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\drop_down.html')
driver.get(file_path)
time.sleep(2)
#先定位到下拉框
m=driver.find_element_by_id("ShippingMethod")
#再点击下拉框下的选项
m.find_element_by_xpath("//option[@value='10.69']").click()
time.sleep(3)
driver.quit()

alert、confirm、prompt的处理

  • text返回alert、confirm、prompt中的文字信息
  • accept点击确认按钮
  • dismiss点击取消按钮

# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
import os
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:\\Users\\320S-15\\seleniumTestHTML\\alert.html')
dr.get(file_path)
# 点击链接弹出alert
dr.find_element_by_id('tooltip').click()
sleep(2)
alert = dr.switch_to.alert
alert.accept()
sleep(2)
dr.quit()

#coding:utf-8
from selenium import webdriver
from time import sleep
import os
driver=webdriver.Chrome()
driver.implicitly_wait(30)
file_path = 'file:///' + os.path.abspath('D:/Users/320S-15/seleniumTestHTML/send.html')
driver.get(file_path)
#点击“请点击”
driver.find_element_by_xpath("html/body/input").click()
#输入内容
driver.switch_to.alert.send_keys('webdriver')
driver.switch_to.alert.accept()
sleep(5)
driver.quit()

div对话框的处理

更多的时候我们在实际应用中碰到的并不是简单的警告框,而是提供更多功能的会话框

# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
import os
import selenium.webdriver.support.ui as ui
dr = webdriver.Chrome()
file_path = 'file:///' + os.path.abspath('D:/Users/320S-15/seleniumTestHTML/modal.html')
dr.get(file_path)
# 打开对话框
dr.find_element_by_id('show_modal').click()
sleep(3)
# 点击对话框中的链接
link = dr.find_element_by_id('myModal').find_element_by_id('click')
link.click()
#dr.execute_script('$(arguments[0]).click()', link)
sleep(4)
# 关闭对话框
buttons =dr.find_element_by_class_name('modal-footer').find_elements_by_tag_name('button')
buttons[0].click()
sleep(2)
dr.quit()

上传文件操作

上传文件过程一般要打开一个本地窗口,从窗口选择本地文件添加。所以selenium webdriver实现的方法是:只要定位上传按钮,通过send_keys添加本地文件路径就可以了,绝对路径和相对路径都可以,关键是上传的文件存在。

#coding=utf-8
from selenium import webdriver
import os,time
driver = webdriver.Chrome()
#脚本要与upload_file.html 同一目录
file_path = 'file:///' + os.path.abspath('D:/Users/320S-15/seleniumTestHTML/upload.html')
driver.get(file_path)
#定位上传按钮,添加本地文件
driver.find_element_by_name("file").send_keys('E:\\320S-15\\test\\baidu.py')
time.sleep(2)
driver.quit()

unittest框架解析

unittest是Python的单元测试,它提供了创建测试用例、测试套件以及批量执行的方案。

作为单元测试的框架,unittest也可以对程序最小模块的一种敏捷化的测试。在自动化测试中,我们虽然不需要做白盒测试,但是必须知道所使用语言的单元测试框架。利用单元测试框架,创建一个类,该类继承了unittest的TestCase,这样可以把每个case看成是一个最小的单元,有测试容器组织起来,到时候直接执行,同时引入测试报告。

unittest各组件的关系如图:

  • Test Fixture:测试固件,初始化和清理测试环境,比如创建临时的数据库、文件和目录等,其中setUp和tearDown是最常用的
  • TestCase:单元测试用例,是编写单元测试用例常用的类
  • TestSuite:单元测试用例的集合,TestSuite也是常用的类
  • TestRunner:执行单元测试
  • TestReport:生成测试报告

这里我们简单的创建一个测试用例:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time
class Baidu1(unittest.TestCase):def setUp(self):print(u"setUp方法")self.driver = webdriver.Chrome()self.driver.implicitly_wait(30)self.base_url = "http://www.baidu.com/"self.verificationErrors = []self.accept_next_alert = True#test fixture,清除环境def tearDown(self):print(u"tearDown方法")self.driver.quit()self.assertEqual([],self.verificationErrors)def test_hao(self):driver = self.driverdriver.get(self.base_url + "/")driver.find_element_by_link_text("hao123").click()time.sleep(6)self.assertEqual(u"hao123_上网从这里开始", driver.title)# @unittest.skip("skipping")def test_baidusearch(self):driver = self.driverdriver.get(self.base_url + "/")driver.find_element_by_id("kw").click()driver.find_element_by_id("kw").clear()driver.find_element_by_id("kw").send_keys(u"雪之城")driver.find_element_by_id("su").click()#判断element是否存在,可删除def is_element_present(self, how, what):try:self.driver.find_element(by=how, value=what)except NoSuchElementException as e:return Falsereturn True#关闭alert,可删除def is_alert_present(self):try: self.driver.switch_to.alertexcept NoAlertPresentException as e:return Falsereturn Truedef close_alert_and_get_its_text(self):try:alert = self.driver.switch_to.alertalert_text = alert.textif self.accept_next_alert:alert.accept()else:alert.dismiss()return alert_textfinally: self.accept_next_alert = Trueif  __name__ == "__main__":unittest.main(verbosity=2)

可以增加verbosity参数,例如unittest.main(verbosity=2)。在主函数中,直接调用main() ,在main中加入verbosity=2 ,这样测试的结果就会显示的更加详细。这里的verbosity 是一个选项, 表示测试结果的信息复杂度,有三个值:

  • 0 ( 静默模式): 你只能获得总的测试用例数和总的结果比如总共100个,失败20 成功80。
  • 1 ( 默认模式): 非常类似静默模式只是在每个成功的用例前面有个“ . ” 每个失败的用例前面有个“F”。
  • 2 ( 详细模式): 测试结果会显示每个测试用例的所有相关的信息。

批量执行脚本

构建测试套件

完整的单元测试需要执行很多个测试用例,开发人员通常需要编写多个测试用例才能对某一软件的功能进行比较完整的测试,这些相关的测试用例称为一个测试用例集,在unittest中是用TestSuite类表示的。

如果我们编写了testbaidu1.py和testbaidu2.py两个文件,那我们怎么同时执行这两个文件呢?

# encoding: utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
class Baidu2(unittest.TestCase):
#test fixture,初始化环境def setUp(self):print(u"setUp方法")self.driver = webdriver.Chrome()self.driver.implicitly_wait(30)self.base_url = "http://www.baidu.com/"self.verificationErrors = []self.accept_next_alert = True#test fixture,清除环境def tearDown(self):print(u"tearDown方法")self.driver.quit()self.assertEqual([], self.verificationErrors)#测试用例,必须以test开头def test_baidusearch(self):driver = self.driverdriver.get(self.base_url + "/")driver.find_element_by_id("kw").click()driver.find_element_by_id("kw").clear()driver.find_element_by_id("kw").send_keys(u"selenium")driver.find_element_by_id("su").click()driver.find_element_by_id("su").click()#判断element是否存在,可删除def is_element_present(self, how, what):try: self.driver.find_element(by=how, value=what)except NoSuchElementException as e:return Falsereturn True#判断alert是否存在,可删除def is_alert_present(self):try: self.driver.switch_to_alert()except NoAlertPresentException as e:return Falsereturn True#关闭alert,可删除def close_alert_and_get_its_text(self):try:alert = self.driver.switch_to.alertalert_text = alert.textif self.accept_next_alert:alert.accept()else:alert.dismiss()return alert_textfinally: self.accept_next_alert = Trueif __name__ == "__main__":
#执行用例unittest.main(verbosity=2)
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time
class Baidu1(unittest.TestCase):def setUp(self):print(u"setUp方法")self.driver = webdriver.Chrome()self.driver.implicitly_wait(30)self.base_url = "http://www.baidu.com/"self.verificationErrors = []self.accept_next_alert = True#test fixture,清除环境def tearDown(self):print(u"tearDown方法")self.driver.quit()self.assertEqual([],self.verificationErrors)def test_hao(self):driver = self.driverdriver.get(self.base_url + "/")driver.find_element_by_link_text("hao123").click()time.sleep(6)self.assertEqual(u"hao123_上网从这里开始", driver.title)# @unittest.skip("skipping")def test_baidusearch(self):driver = self.driverdriver.get(self.base_url + "/")driver.find_element_by_id("kw").click()driver.find_element_by_id("kw").clear()driver.find_element_by_id("kw").send_keys(u"雪之城")driver.find_element_by_id("su").click()#判断element是否存在,可删除def is_element_present(self, how, what):try:self.driver.find_element(by=how, value=what)except NoSuchElementException as e:return Falsereturn True#关闭alert,可删除def is_alert_present(self):try: self.driver.switch_to.alertexcept NoAlertPresentException as e:return Falsereturn Truedef close_alert_and_get_its_text(self):try:alert = self.driver.switch_to.alertalert_text = alert.textif self.accept_next_alert:alert.accept()else:alert.dismiss()return alert_textfinally: self.accept_next_alert = Trueif  __name__ == "__main__":unittest.main(verbosity=2)

这里就需要构建测试套件,构建测试套件有三种方法,不同的情景下用不同的方法:

  • 直接加入测试方法:addTest()
  • makeSuit()/TestLoader()
  • discover()

addTest()的应用

当有多个或几百个测试用例的时候,就需要一个测试容器(测试套件),把测试用例放在容器中进行执行,unittest模块中提供了addSuite类来生成测试套件,使用该类的构造函数可以生成一个测试套件的实例,该类提供了addTest把每个测试用例加入到测试套件中。

将testbaidu1.py,testbaidu2.py,runall.py放在同一级目录下,runall.py文件如下:

# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#导入testbaidu1,testbaidu2
import testbaidu1
import testbaidu2
#手工添加案例到套件,
def createsuite():suite = unittest.TestSuite()#将测试用例加入到测试容器(套件)中suite.addTest(testbaidu1.Baidu1("test_baidusearch"))suite.addTest(testbaidu1.Baidu1("test_hao"))suite.addTest(testbaidu2.Baidu2("test_baidusearch"))return suiteif __name__=="__main__":suite = createsuite()runner = unittest.TextTestRunner(verbosity=2)runner.run(suite)

上述做法有两个不方便的地方,阻碍脚本的快速执行,必须每次修改runall.py:
1)需要导入所有的py文件,比如import testbaidu1,每新增一个需要导入一个
2)addTest需要增加所有的testcase,如果一个py文件中有10个case,就需要增加10次

于是就引入了makeSuit()/TestLoader()

makeSuite()和TestLoader()

在unittest 框架中提供了makeSuite() 的方法,makeSuite可以实现把测试用例类内所有的测试case组成的测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。TestLoader 用于创建类和模块的测试套件,一般的情况下TestLoader().loadTestsFromTestCase(TestClass)来加载测试类。

修改runall.py

# -*- coding: utf-8 -*-
import unittest
import time
import testbaidu1
import testbaidu2
#手工添加案例到套件,
def createsuite():#suite = unittest.TestSuite()#将测试用例加入到测试容器(套件)中######### makeSuite() ############### # suite.addTest(unittest.makeSuite(testbaidu1.Baidu1))# suite.addTest(unittest.makeSuite(testbaidu2.Baidu2))# return suite########## TestLoader() ##############suite1 = unittest.TestLoader().loadTestsFromTestCase(testbaidu1.Baidu1)suite2 = unittest.TestLoader().loadTestsFromTestCase(testbaidu2.Baidu2)suite = unittest.TestSuite([suite1, suite2])return suiteif __name__=="__main__":suite = createsuite()runner = unittest.TextTestRunner(verbosity=2)runner.run(suite)

经过makeSuite()和TestLoader()的引入,我们不用一个py文件测试类,只需要导入一次即可。
那么能不能测试类也不用每次添加指定呢?

discover()的应用

discover 是通过递归的方式到其子目录中从指定的目录开始, 找到所有测试模块并返回一个包含它们对象的TestSuite ,然后进行加载与模式匹配唯一的测试文件,discover 参数分别为discover(dir,pattern,top_level_dir=None)

__author__ = '320S-15'
# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#手工添加案例到套件,
def createsuite():discover=unittest.defaultTestLoader.discover('../test',pattern='testbaidu*.py',top_level_dir=None)print discoverreturn discoverif __name__=="__main__":suite=createsuite()runner = unittest.TextTestRunner(verbosity = 2)runner.run(suite)

unittest 框架默认加载测试用例的顺序是根据ASCII 码的顺序,数字与字母的顺序为: 0~9,A~Z,a~z 。所以, TestAdd 类会优先于TestBdd 类被发现, test_aaa() 方法会优先于test_ccc() 被执行。对于测试目录与测试文件来说, unittest 框架同样是按照这个规则来加载测试例。

如果想忽略某个用例的执行,可在方法前加@unittest.skip("skipping")

unittest断言

自动化测试中,对于每个单独的case来说,一个case执行结果中,必然会有期望结果与实际结果,来判断该case是通过还是失败。在unittest的库中提供了大量实用方法来检查预期值与实际值来验证case的结果。一般来说,检查条件大体分为等价性、逻辑比较以及其他,如果给定的断言通过,测试会继续执行到下一行的代码,如果断言失败,对应的case测试会立即停止或者生成错误信息(一般可以打印错误信息即可),但不要影响其他的case执行。

unittest的单元测试提供了标准的XUnit断言方法。下面是一些常见的断言:

断言方法 断言描述
assertEqual(arg1,arg2,msg=None) 验证arg1 = arg2,不等则fail
assertNotEqual(arg1,arg2,msg=None) 验证arg1 != arg2,不等则fail
assertTrue(exxpr,msg=None) 验证expr是true,如果false,则fail
assertFalse(exxpr,msg=None) 验证expr是false,如果true,则fail
assertIs(arg1, arg2, msg=None)  验证arg1、arg2是同一个对象,不是则fail
assertIsNot(arg1, arg2, msg=None)  验证arg1、arg2不是同一个对象,是则fail
assertIsNone(expr, msg=None)  验证expr是None,不是则fail
assertIsNotNone(expr, msg=None)  验证expr不是None,是则fail
assertIn(arg1, arg2, msg=None) 验证arg1是arg2的子串,不是则fail
assertNotIn(arg1, arg2, msg=None) 验证arg1不是arg2的子串,是则fail
assertIsInstance(obj, cls, msg=None)  验证obj是cls的实例,不是则fail
 assertNotIsInstance(obj, cls, msg=None) 验证obj不是cls的实例,是则fail

如前面的例子中:self.assertEqual(u"hao123_上网从这里开始", driver.title)

HTML报告生成

执行完脚本之后,还需要看到HTML报告,可以通过HTMLTestRunner.py生成测试报告。因为我本机安装的是Python2.7,所以需要下载HTMLTestRunner.py文件,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html。下载后将其放在testcase目录下或放入安装路径...\Python2.7\Lib目录下。

将上面的runall.py改成:

# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
import HTMLTestRunner
#手工添加案例到套件,
def createsuite():discover=unittest.defaultTestLoader.discover('../test',pattern='testbaidu*.py',top_level_dir=None)print discoverreturn discover
if __name__=="__main__":curpath=sys.path[0]print(sys.path)print("=================")print(sys.path[0])#取当前时间now=time.strftime("%Y-%m-%d-%H %M %S",time.localtime(time.time()))if not os.path.exists(curpath+'/resultreport'):os.makedirs(curpath+'/resultreport')print("=================")print(time.time())print("=================")print(time.localtime(time.time()))print("=================")print(now)#在当前目录下创建一个子目录resultreport,然后在该子目录下生成一个resultreport.html文件filename=curpath+'/resultreport/'+now+'resultreport.html'with open(filename,'wb') as fp:#生成html报告runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'测试报告',description=u'用例执行情况',verbosity=2)suite=createsuite()runner.run(suite)

测试报告用浏览器打开如图所示:

异常捕捉和错误截图

用例不可能每次都运行成功,也有运行不成功的时候,如果可以捕捉错误,这样就能更方便我们定位错误,因此unittest中有一个函数get_screenshot_as_file可以实现错误截图功能。

脚本实现实例:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
import os
class Baidu1(unittest.TestCase):#test fixture,初始化环境def setUp(self):self.driver = webdriver.Chrome()self.driver.implicitly_wait(30)self.base_url = "http://www.baidu.com/"self.verificationErrors = []self.accept_next_alert = True#测试用例,必须以test开头def test_hao(self):driver = self.driverdriver.get(self.base_url)driver.find_element_by_link_text("hao123").click()time.sleep(2)try:self.assertEqual(u'hao123_上网从这里开始', driver.title)except:self.savescreenshot(driver,'hao.png')#判断element是否存在,可删除def is_element_present(self, how, what):try: self.driver.find_element(by=how, value=what)except NoSuchElementException as e:return Falsereturn True#判断alert是否存在,可删除def is_alert_present(self):try: self.driver.switch_to.alertexcept NoAlertPresentException as e:return Falsereturn True#关闭alert,可删除def close_alert_and_get_its_text(self):try:alert = self.driver.switch_to.lertalert_text = alert.textif self.accept_next_alert:alert.accept()else:alert.dismiss()return alert_textfinally: self.accept_next_alert = True#test fixture,清除环境def tearDown(self):self.driver.quit()self.assertEqual([], self.verificationErrors)#截图def savescreenshot(self,driver,file_name):if not os.path.exists('./image'):#如果不存在image文件夹,我们就创建之os.makedirs('./image')now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))print(time.time())print(time.localtime(time.time()))print(now)#截图保存driver.get_screenshot_as_file('./image/'+now+'-'+file_name)time.sleep(1)if __name__ == "__main__":
#执行用例unittest.main(verbosity=2)

如下就是捕捉到的错误截图:

数据驱动

如果需要多次执行一个案例,比如百度搜索,分别输入中文、英文、数字,或者同类型的多个数据,这时我们就想实现一次运行多个测试用例、unittest没有自带数据驱动功能,所以如果使用unittest,同时又要使用数据驱动,那么就可以使用ddt来完成。

需要安装ddt,可以在控制台输入如下命令:

pip install ddt

python setup.py install

ddt的使用方法

dd.ddt

装饰类,也就是继承自TestCase的类。

ddt.data:
装饰测试方法。参数是一系列的值。
ddt.file_data:
装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。
注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件处理。
如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。
如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。
ddt.unpack:
传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上。字典也可以这样处理。

实现一个Testddt类,测试ddt.data

#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
import os,sys,csv
from ddt import ddt, data, unpack ,file_data
def getCsv(file_name):rows=[]path=sys.path[0].replace('\test','')print(path)with open(path+'/data/'+file_name,'rb') as f:readers=csv.reader(f,delimiter=',',quotechar='|')next(readers,None)for row in readers:temprows=[]for i in row:temprows.append(i.decode('gbk'))rows.append(temprows)return rows
#引入ddt
@ddt
class Testddt(unittest.TestCase):def setUp(self):self.driver = webdriver.Chrome()self.driver.implicitly_wait(30)self.base_url = "http://www.baidu.com"self.verificationErrors = []self.accept_next_alert = True@data(*getCsv('test_baidu_data.csv'))@unpackdef test_hao(self,value,expected_value):driver = self.driverdriver.get(self.base_url + "/")driver.find_element_by_id("kw").clear()driver.find_element_by_id("kw").send_keys(value)driver.find_element_by_id("su").click()time.sleep(2)print(value)self.assertEqual(expected_value, driver.title)print expected_valueprint driver.title#判断element是否存在,可删除def is_element_present(self, how, what):try: self.driver.find_element(by=how, value=what)except NoSuchElementException as e: return Falsereturn True#判断alert是否存在,可删除def is_alert_present(self):try: self.driver.switch_to.alertexcept NoAlertPresentException as e:return Falsereturn True#关闭alert,可删除def close_alert_and_get_its_text(self):try:alert = self.driver.switch_to.alertalert_text = alert.textif self.accept_next_alert:alert.accept()else:alert.dismiss()return alert_textfinally: self.accept_next_alert = True#test fixture,清除环境def tearDown(self):self.driver.quit()self.assertEqual([], self.verificationErrors)def savescreenshot(self,driver,file_name):if not os.path.exists('./image'):os.makedirs('./image')now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))#截图保存driver.get_screenshot_as_file('./image/'+now+'-'+file_name)time.sleep(1)
if __name__ == "__main__":
#执行用例unittest.main(verbosity=2)

test_baidu_data.csv:

data
周迅,周迅_百度搜索
林允,林允_百度搜索

测试ddt.file_data

#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re
import os,sys,csv
from ddt import ddt, data, unpack ,file_data
@ddt
class Testddt(unittest.TestCase):def setUp(self):self.driver = webdriver.Chrome()self.driver.implicitly_wait(30)self.base_url = "http://www.baidu.com"self.verificationErrors = []self.accept_next_alert = True@file_data('test_baidu_data.json')def test_hao(self,value):driver = self.driverdriver.get(self.base_url + "/")driver.find_element_by_id("kw").clear()driver.find_element_by_id("kw").send_keys(value)driver.find_element_by_id("su").click()time.sleep(2)print(value)# self.assertEqual(expected_value, driver.title)# print expected_valueprint driver.title#判断element是否存在,可删除def is_element_present(self, how, what):try: self.driver.find_element(by=how, value=what)except NoSuchElementException as e: return Falsereturn True#判断alert是否存在,可删除def is_alert_present(self):try: self.driver.switch_to.alertexcept NoAlertPresentException as e:return Falsereturn True#关闭alert,可删除def close_alert_and_get_its_text(self):try:alert = self.driver.switch_to.alertalert_text = alert.textif self.accept_next_alert:alert.accept()else:alert.dismiss()return alert_textfinally: self.accept_next_alert = True#test fixture,清除环境def tearDown(self):self.driver.quit()self.assertEqual([], self.verificationErrors)def savescreenshot(self,driver,file_name):if not os.path.exists('./image'):os.makedirs('./image')now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))#截图保存driver.get_screenshot_as_file('./image/'+now+'-'+file_name)time.sleep(1)
if __name__ == "__main__":
#执行用例unittest.main(verbosity=2)

test_baidu_data.json:

["hello","selenium"
]

Selenium自动化测试工具的介绍与使用相关推荐

  1. Python高级特性与网络爬虫(二):使用Selenium自动化测试工具爬取一号店商品信息

    上一篇介绍了Ajax动态渲染的页面的分析和爬取,通过JavaScript动态渲染的页面的方式不只有ajax这一种,还有很多其他的方式,分析他们的网页结构和加密参数难度非常大,为了解决这样的页面的数据爬 ...

  2. 如何使用Selenium自动化测试工具获取动态图片验证码?

    关于图片验证码的文章,我想大家都应该看过类似的文章了. 在我们做UI自动化的时候,经常会遇到图片验证码的问题. 当开发不给咱们提供万能验证码,或者测试第三方网站比如知乎的时候,我们就需要自己去识别验证 ...

  3. 第六章 selenium自动化测试工具:天下工具为我所用

    简介 Selenium是一个Web的自动化测试工具,类型像我们玩游戏用的按键精灵,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器). Selenium 可以根据我们的指令,让浏览器自 ...

  4. 禅道与selenium自动化测试工具

    文章目录 一.禅道 1.禅道是什么工具? 二.selenium 1.什么是自动化测试? 2.自动化测试工具(为什么要选择selenium工具) 3.自动化测试的分类 4.自动化测试的优势 5.什么项目 ...

  5. Selenium自动化测试工具的简单介绍和使用

    Selenium Selenium what:进行web网站的自动化测试,提供了操控网页的功能,包括向网页中put和从网页中get how:提供了库,针对不同语言提供了不同语言的库 一.自动化原理 三 ...

  6. 比selenium体验更好的ui自动化测试工具: cypress介绍

    话说 Cypress is a next generation front end testing tool built for the modern web. And Cypress can tes ...

  7. selenium 自动化测试工具(二)常用定位方式

    注册A的页面 <!DOCTYPE html> <html lang="zh-cn"> <head><meta charset=" ...

  8. 【Python爬虫】<万物可爬>Selenium+自动化测试工具 获取数据

    目录 第一个爬虫程序: Web请求的全过程剖析: HTTP协议: 请求: 请求头中常见的重要内容: 请求方式: 响应: Requests: 数据解析: 数据提取的三种解析方式: 正则表达式: 爬取案例 ...

  9. 自动化测试工具Selenium

    本篇文章将介绍selenium自动化测试工具.看完这篇文章,完全有能力写出一个抢课脚本.文章最后有一个教务网的实战案例,包括验证码识别,自动化处理流程分析. 1.环境搭建 1.1下载驱动程序 以谷歌浏 ...

最新文章

  1. Android之对话框Dialog的创建
  2. 原生JS实现图片滚动
  3. 解决 /usr/share/git-cola/bin/ssh-askpass: 3: exec: wish: not found
  4. linux实用小工具,Linux系统小工具使用大用途---1
  5. 排序算法 —— 快速排序
  6. 序列标注问题中将分类标签引入RNN网络结构的尝试
  7. MM(Majorize-Minimization, Minorize-Maximization)优化方法
  8. 三元运算符 在数据绑定中的使用
  9. java try的用法_Java中try、catch的使用方法
  10. PTA19、通过两个列表构建字典 (10 分)
  11. python入门小程序代码_Python入门小程序(二)
  12. cookie注入原理详解(一)
  13. 蓝桥杯省赛2020 成绩统计
  14. html怎样制作表格模板,Word怎么做入职表格模板
  15. 维谛技术(Vertiv)隆重举行“笃行——数据中心基础设施智能化管理研讨会”...
  16. Foxmail接受密码错误
  17. 王兄弟跟妹纸分享逆袭秘籍,无关风月!
  18. 常用函数的拉氏变换表
  19. 当应酬成为日常,你需要这20个技巧聪明地进食
  20. 那些可以看做是沧海一粟的App

热门文章

  1. 2.4. Prompting
  2. Java实现 LeetCode 838 推多米诺(暴力模拟)
  3. 获取HTML中文本框的值并进行比较两者的大小
  4. 2020机修钳工(中级)考试及机修钳工(中级)复审模拟考试
  5. 计算机未来目标规划,计算机专业职业生涯规划范文
  6. 用C语言程序算交税,用C语言编写函数InComeTax计算七级累进税率的税后收入
  7. 完全零基础小白,应该怎样学习编程呢?
  8. 简单的VUE购物车应用
  9. SQL求用户的最大连续登陆天数
  10. Scipy-kmeans聚类色彩提取