写在最前面:目前自动化测试并不属于新鲜的事物,或者说自动化测试的各类方法论已经层出不穷,可是,可以在项目中锲而不舍的实践自动化测试的团队,却依旧不是很是多。有的团队知道怎么作,作的还不够好;有的团队还正在探索和摸索怎么作,甚至还有一些多方面的技术上和非技术上的旧系统须要重构……
本文将会从使用
和实践
两个视角,尝试对基于Web UI
自动化测试作细致的分析和解读,给各位去思考和实践作一点引路,以便各团队能找到更好的方式。
《论语》有云:工欲善其事,必先利其器。在开始具体的自动化测试以前,咱们须要作好更多的准备,包括如下几个方面:css
1. 自动化测试理论介绍html
1.1 自动化测试的5W
前端
正如开篇所提到的,自动化测试再也不是一个陌生的话题,而是一个具体的存在。做为测试实践活动的一部分,咱们首先分析一下自动化测试的方方面面。node
WHAT
, 什么是自动化测试G.J.Myers在其经典的著做《软件测试艺术》(The Art of Software Testing)一书中,给出了测试的定义:“程序测试是为了发现错误而执行的过程。”python
这个概念产生于30年前,对软件测试的认识还很是有局限性,固然也是由于受瀑布开发模型的影响,认为软件测试是编程以后的一个阶段。只有等待代码开发出来之后,经过执行程序,像用户那样操做软件去发现问题。
自动化测试:以人为驱动的测试行为转化为机器执行的一种过程
自动化测试,就是把手工进行的测试过程,转变成机器自动执行的测试过程。该过程,依旧是为了发现错误而执行。所以自动化测试的关键在于“自动化”三个字。自动化测试的内容,也就相应的转变成如何“自动化”去实现本来手工进行的测试的过程。
全部的“自动化”,依靠的无疑都是程序。
经过程序,能够把手工测试,转变成自动化测试。mysql
WHEN
, 在何时开展自动化测试 自动化测试的开展,依赖于“程序”。那么程序,其实就是由“源代码”构建而来的。那么原则上,只要能作出自动化测试所须要的“程序”的时候,变能够进行自动化测试。但每每,并非全部的“时候”都是好的“时机”。从这个W
开始,咱们将会加入对于成本的顾虑,也正是由于“成本”的存在,才使得下面的讨论,变得有意义。
全部的开销,都是有成本的。构建成“程序”的源代码,也是由工程师写出来的。那么须要考虑这个过程当中的成本。基于这个考虑,在可以比较稳定的构建“程序”的时候,不须要花费太多开销在“源代码”的时候,就是开展自动化测试的好时机。这个开销包括编写
和修改
源代码,而源代码指的是构建出用来作自动化测试的程序
的源代码。git
WHERE
, 在什么地方进行自动化测试 自动化测试的执行,依靠的是机器。那么自动化测试必将在“机器”上进行。通常来讲,这个机器包括桌面电脑和服务器。经过将写好的源代码
部署在机器上,构建出用来作自动化测试的"程序",而且运行该程序,实现自动化测试。github
WHICH
, 对什么目标进行自动化测试自动化测试的目标,是被测试的软件。抛开人工智能的成分,手工测试必将在“人工智能”足够普及和足够“智能”以前,替代一大部分不须要“人类智能”的手工测试;以及自动化测试会作一些手工测试没法实施的,或者手工测试没法覆盖的测试。不须要“人类智能”的普通手工测试:web
HOW
, 如何开展自动化测试和全部的其余测试同样,自动化测试的流程也是由“用例”执行和“缺陷”验证组成。差异是须要找到合适的“工具”来替代“人手”。不一样目标的自动化测试有不一样的测试工具,可是任何工具都无不例外的须要“编程”的过程,实现“源代码”,也能够称之为测试脚本。因而开展自动化测试的方式基本上以下:sql
2. 自动化测试的典型金字塔原理
谈到自动化测试,就不得不提的一我的和概念就是:Martin Fowler和他的金字塔原理。首先请看金字塔原理的图示以下:
该图说明了三个问题:
这是理想中的金字塔原理。
在实际的项目中,尤为是结合国内的项目实践,其实还隐藏了另外一个问题:越是顶层的测试,效果越明显。有句话说“贵的东西,除了贵,其余都是好的!”可以很清晰的阐述这个观点。
金字塔原理在国内的适应性也有必定的问题
相对来讲,在基于UI前端界面的自动化测试反却是开展和实施的不是特别多。尽管基于界面的测试带来的效果仍是可以立竿见影的。对于产品的质量提高,仍是比较容易有保证。
自动化测试能够涉及和试用的范围主要在如下方面:
Web UI
的浏览器应用的界面测试WebService
或者WebAPI
的服务契约测试WCF
、.net remoting
、Spring
等框架的服务的集成测试APP UI
的移动应用界面测试Java
、C#
等编程文件进行的单元测试本文集中讨论第一条:基于Web UI
的浏览器应用的界面测试。界面的改动对于测试来讲,具备较大的成本风险。主要考虑如下方面:
自动化测试和普通的手工测试遵循的测试流程,与项目的具体实践相关。通常来讲,也是须要从测试计划开始涉及自动化测试的。
3. 自动化测试工具
基于Web UI
的自动化测试工具主要有两大类:付费的商业版工具和无偿使用的开源版工具。典型的有两种:
3.1 Selenium 基本介绍
Selenium`是开源的自动化测试工具,它主要是用于Web 应用程序的自动化测试,不仅局限于此,同时支持全部基于web 的管理任务自动化。
Selenium
官网的介绍Selenium is a suite of tools to automate web browsers across many platforms.
Selenium 是用于测试 Web 应用程序用户界面 (UI) 的经常使用框架。它是一款用于运行端到端功能测试的超强工具。您可使用多个编程语言编写测试,而且 Selenium 可以在一个或多个浏览器中执行这些测试。
Selenium 经历了三个版本:Selenium 1,Selenium 2 和 Selenium 3。Selenium 也不是简单一个工具,而是由几个工具组成,每一个工具都有其特色和应用场景。
Selenium 诞生于 2004 年,当在 ThoughtWorks
工做的 Jason Huggins 在测试一个内部应用时。做为一个聪明的家伙,他意识到相对于每次改动都须要手工进行测试,他的时间应该用得更有价值。他开发了一个能够驱动页面进行交互的 Javascript 库,能让多浏览器自动返回测试结果。那个库最终变成了 Selenium 的核心,它是 Selenium RC(远程控制)和 Selenium IDE 全部功能的基础。Selenium RC 是开拓性的,由于没有其余产品能让你使用本身喜欢的语言来控制浏览器。这就是 Selenium 1。
然而,因为它使用了基于 Javascript 的自动化引擎,而浏览器对 Javascript 又有不少安全限制,有些事情就难以实现。更糟糕的是,网站应用正变得愈来愈强大,它们使用了新浏览器提供的各类特性,都使得这些限制让人痛苦不堪。
在 2006 年,一名 Google 的工程师, Simon Stewart 开始基于这个项目进行开发,这个项目被命名为 WebDriver。此时,Google 早已经是 Selenium 的重度用户,可是测试工程师们不得不绕过它的限制进行工具。Simon 须要一款能经过浏览器和操做系统的本地方法直接和浏览器进行通话的测试工具,来解决Javascript 环境沙箱的问题。WebDriver 项目的目标就是要解决 Selenium 的痛点。
到了 2008 年,Selenium 和 WebDriver 两个项目合并。Selenium 有着丰富的社区和商业支持,但 WebDriver 显然表明着将来的趋势。二者的合并为全部用户提供了一组通用功能,而且借鉴了一些测试自动化领域最闪光的思想。这就是 Selenium 2。
2016 年,Selenium 3 诞生。移除了再也不使用的 Selenium 1 中的 Selenium RC,而且官方重写了全部的浏览器驱动。
Selenium IDE (集成开发环境) 是一个建立测试脚本的原型工具。它是一个 Firefox 插件,实现简单的浏览器操做的录制与回放功能,提供建立自动化测试的建议接口。Selenium IDE 有一个记录功能,能记录用户的操做,而且能选择多种语言把它们导出到一个可重用的脚本中用于后续执行。
Selenium RC 是selenium 家族的核心工具,Selenium RC 支持多种不一样的语言编写自动化测试脚本,经过selenium RC 的服务器做为代理服务器去访问应用从而达到测试的目的。
selenium RC 使用分Client Libraries 和Selenium Server。
Selenium Grid 使得 Selenium RC 解决方案能提高针对大型的测试套件或者哪些须要运行在多环境的测试套件的处理能力。Selenium Grid 能让你并行的运行你的测试,也就是说,不一样的测试能够同时跑在不一样的远程机器上。这样作有两个有事,首先,若是你有一个大型的测试套件,或者一个跑的很慢的测试套件,你可使用 Selenium Grid 将你的测试套件划分红几份同时在几个不一样的机器上运行,这样能显著的提高它的性能。同时,若是你必须在多环境中运行你的测试套件,你能够得到多个远程机器的支持,它们将同时运行你的测试套件。在每种状况下,Selenium Grid 都能经过并行处理显著地缩短你的测试套件的处理时间。
WebDriver 是 Selenium 2 主推的工具,事实上WebDriver是Selenium RC的替代品,由于Selenium须要保留向下兼容性的缘由,在 Selenium 2 中, Selenium RC才没有被完全的抛弃,若是使用Selenium开发一个新的自动化测试项目,那么咱们强烈推荐使用Selenium2 的 WebDriver进行编码。另外, 在Selenium 3 中,Selenium RC 被移除了。
/'paɪθən/
3.2 JetBrains PyCharm 使用
PyCharm 是 JetBrains 公司针对Python推出的IDE(Integrated Development Environment,集成开发环境)。是目前最好的Python IDE之一。目前包含了两个版本:
咱们推荐使用免费的社区版本,进行Python脚本的编写和自动化测试执行。
PyCharm能够在官网下载,http://www.jetbrains.com
PyCharm 安装后,若是也安装过 Python 环境,能够直接进行操做。不然请在 1.2.3 中安装好 Python,再使用 PyCharm。
建立新的项目,选择项目建立的位置,选择Python的解释器
C:\Pytho34
中,应该放到普通的目录中
新建Python文件
在建立的文件中编写第一个Python语句
print("hello Python!")
右键该文件,选择Run hello
,运行该语句,在下面的运行框中会显示运行结果
C:\Python35\python.exe D:/Git/WeekendSelenium/untitled/hello.py hello python! Process finished with exit code 0
如图
打开已经存在的项目,好比别人发给你的项目,或者已经建立过的项目
在PyCharm 里,显示行号有两种办法:
Show Line Numbers
。
可是这种方法,只对一个文件有效,而且,重启PyCharm 后消失。
File
--> Settings
-->Editor
-->Appearance
, 以后勾选Show Line Numbers
。
选择 Settings | Editor | Colors & Fonts | Fonts
Save AS 主题
选择 Source Code Pro(建议选择,等宽字体)
源代码管理工具(VCS, version control system)
若是TortoiseSVN版本低于 1.8
,须要先升级安装1.8
以上的版本
选择SVN(git)做为代码的源代码管理工具。集成在PyCharm中的步骤以下
D:\SVN\XXProject\Trunck
VCS
|Enable VCS integration
,选择 Subversion(svn) 或者 Git
3.3 Selenium 的环境搭建
主要包括两个步骤:
Python的官方网站:http://www.python.org
Python 目前并行了两套版本,2.x 和 3.x。若是你以前没有 Python 的使用经验,建议使用 Python 3.x 版本。两套版本互相不兼容,而且 Python 从 3.5(含)开始,再也不支持 Windows XP 系统,请注意。
C:\python34
C:\python35
勾选Add Python.exe to PATH
Anaconda3
,这样的包有丰富的第三方库,在使用 Python 的过程当中会更加方便。Anaconda 的官网:https://www.continuum.io/anaconda-overview
因为 安装好的 Python 默认有 pip
Python 包管理工具,能够经过 pip
很是方便的安装 Selenium。
pip install selenium
该命令的执行须要有互联网联网环境。此外该命令有如下几种选项可使用
pip install selenium==3.4.3
pip install -U selenium # -U 也能够用 --upgrade pip install --upgrade selenium
pip uninstall selenium
python setup.py install
ChromeDriver版本支持的Chrome版本v2.31v58-60v2.30v58-60v2.29v56-58v2.28v55-57v2.27v54-56v2.26v53-55v2.25v53-55v2.24v52-54v2.23v51-53v2.22v49-52v2.21v46-50v2.20v43-48
GeckoDriver版本支持的Firefox版本v0.18.0v56v0.17.0v55v0.16.0v54,须要Selenium 3.4或者以上v0.15.0v53,须要Selenium 3.3或者以上
4. Selenium 的最简脚本
经过上一节的环境安装成功之后,咱们能够进行第一个对Selenium 的使用,就是最简脚本编写。脚本以下:
实际上一段20行的代码,也不能算太少了。可是这段代码的使用,确实体现了 Selenium 的最简单的使用。咱们在下面内容进行阐述。
经过前面的介绍,咱们知道 Selenium 支持多种语言,而且推荐使用面向对象的方式进行编程。接下来咱们将着重介绍如何使用面向对象的方式进行编程。
咱们利用 Python 进行面向对象编程,须要首先了解一个概念:类
类是任何面向对象编程的语言的基本组成,描述了使用的基本方法。咱们可能在目前,还不是特别明白类的含义,可是咱们能够经过类的使用,来进一步了解。
类,经过实例化进行使用。好比有一个类: Driver
,该类有一个方法: head(road)
那么关于这个类的使用,只须要两个步骤:
d = Driver()
d.head("中山路")
在面向对象的理念看来,任何的编码,都是由对象而来的,这里也不例外。和以前介绍 WebDriver 时候的描述对应,咱们须要用到两种主要的类,并将其实例化。
上述代码中,使用了一个 WebDriver 类 的对象,即第2行,声明了该类的对象,并赋值给变量 driver,接着变量 driver 做为 WebDriver 类的对象,使用了多个 WebDriver 类的方法。
注意:Chrome 是 WebDriver 的子类,是 WebDriver 类的一种
we_account
,we_password
和最后一个匿名的对象,并经过产生的三个对象,调用 WebElement 类的方法
正是经过这样的面向对象的方式,产生 Web司机(WebDriver类的对象),而且经过 Web司机不懈的努力,寻找到各类 Web元素(WebElement类的对象)进行操做,这样便实现了 Selenium WebDriver 做为一款出色的浏览器测试工具,进行浏览器UI界面的自动化测试的代码编写和用例执行。
5. Selenium WebDriver API 的使用
经过上述最简脚本的使用,咱们能够来进一步了解 Selenium 的使用。事实上,上一节用的,即是 Selenium 的 WebDriver API。API(Application Programming Interface,应用程序编程接口,即经过编程语言,操做 WebDriver 的方法集合)
Selenium WebDriver API 官方参考:http://seleniumhq.github.io/selenium/docs/api/py/
具体API文档地址:https://seleniumhq.github.io/selenium/docs/api/py/api.html
5.1 控制浏览器
浏览器的控制也是自动化测试的一个基本组成部分,咱们能够将浏览器最大化,设置浏览器的高度和宽度以及对浏览器进行导航操做等。
5.2 元素定位操做
WebDriver提供了一系列的定位符以便使用元素定位方法。常见的定位符有如下几种:
那么咱们如下的操做将会基于上述的定位符进行定位操做。
对于元素的定位,WebDriver API能够经过定位简单的元素和一组元素来操做。在这里,咱们须要告诉Selenium如何去找元素,以致于他能够充分的模拟用户行为,或者经过查看元素的属性和状态,以便咱们执行一系列的检查。
在Selenium2中,WebDriver提供了多种多样的find_element_by
方法在一个网页里面查找元素。这些方法经过提供过滤标准来定位元素。固然WebDriver也提供了一样多种多样的find_elements_by
的方式去定位多个元素。
尽管上述的方式,能够进行元素定位,实际上咱们也是更多的用组合的方式进行元素定位。
接下来的列表将会详细展现find_elements_by
的方法集合。这些方法依据匹配的具体标准返回一系列的元素。(注意区别是element和elements)
5.2.1 依据ID查找
请查看以下HTML的代码,以便实现经过ID的属性值去定义一个查找文本框的查找:
根据上述代码,这里咱们使用find_element_by_id()
的方法去查找搜索框而且检查它的最大长度maxlength
属性。咱们经过传递ID的属性值做为参数去查找,参考以下的代码示例:
若是使用find_elements_by_id()
方法,将会返回全部的具备相同ID属性值的一系列元素。
5.2.2 依据名称name查找
这里仍是根据上述ID查找的HTML代码,使用find_element_by_name
的方法进行查找。参考以下的代码示例:
一样,若是使用find_elements_by_name()
方法,将会返回全部的具备相同name属性值的一系列元素。
5.2.3 依据class name查找
除了上述的ID和name的方式查找,咱们还可使用class name的方式进行查找和定位。
事实上,经过ID,name或者类名class name查找元素是最提倡推荐的和最快的方式。固然Selenium2 WebDriver也提供了一些其余的方式,在上述三类方式条件不足,查找无效的时候,能够经过这些其余方式来查找。这些方式将会在后续的内容中讲述。
请查看以下的HTML代码,经过改代码进行练习和理解。
根据上述代码,使用find_element_by_class_name()
方法去定位元素。
一样的若是使用find_elements_by_class_name()
方法去定位元素,将会返回全部的具备相同name属性值的一系列元素。
5.2.4 依据标签名tag name查找
利用标签的方法相似于利用类名等方法进行查找。咱们能够轻松的查找出一系列的具备相同标签名的元素。例如咱们能够经过查找表中的<tr>
来获取行数。
下面有一个HTML的示例,这里在无序列表中使用了<img>
标签。
这里面咱们使用find_elements_by_tag_name()
的方式去获取所有的图片,在此以前,咱们将会使用find_element_by_class_name()
去获取到指定的<ul>
。
具体代码以下:
5.2.5依据连接文字link查找
连接文字查找一般比较简单。使用find_element_by_link_text
请查看如下示例
测试代码以下:
5.2.6依据部分连接文字partial text查找
这里依旧使用上述的列子进行代码编写:
5.2.7依据XPath进行查找
XPath是一种在XML文档中搜索和定位节点node的一种查询语言。全部的主流Web浏览器都支持XPath。Selenium2能够用强大的XPath在页面中查找元素。
经常使用的XPath的方法有starts-with(),contains()和ends-with()等
若想要了解更多关于XPath的内容,请查看http://www.w3schools.com/XPath/
以下有一段HTML代码,其中里面的<img>没有使用ID,name或者类属性,因此咱们没法使用以前的方法。亚这里咱们能够经过<img>的alt属性,定位到指定的tag。
1 <ul class="promos"> 2 <li> 3 <a href="http://demo.magentocommerce.com/home-decor.html"> 4 <img src="/media/wysiwyg/homepage-three-column-promo- 5 01B.png" alt="Physical & Virtual Gift Cards"> 6 </a> 7 </li> 8 <li> 9 <a href="http://demo.magentocommerce.com/vip.html"> 10 <img src="/media/wysiwyg/homepage-three-column-promo- 11 02.png" alt="Shop Private Sales - Members Only"> 12 </a> 13 </li> 14 <li> 15 <a href="http://demo.magentocommerce.com/accessories/ 16 bags-luggage.html"> 17 <img src="/media/wysiwyg/homepage-three-columnpromo- 18 03.png" alt="Travel Gear for Every Occasion"> 19 </a> 20 </li> 21 </ul>
具体代码以下:
1 def test_vip_promo(self): 2 # get vip promo image 3 vip_promo = self.driver.\ 4 find_element_by_xpath("//img[@alt='Shop Private Sales - Members Only']") 5 # check vip promo logo is displayed on home page 6 self.assertTrue(vip_promo.is_displayed()) 7 # click on vip promo images to open the page 8 vip_promo.click() 9 # check page title 10 self.assertEqual("VIP", self.driver.title)
固然,若是使用find_elements_by_xpath()
的方法,将会返回全部匹配了XPath查询的元素。
5.2.8 依据CSS选择器进行查找
CSS是一种设计师用来描绘HTML文档的视觉的层叠样式表。通常来讲CSS用来定位多种多样的风格,同时能够用来是一样的标签使用一样的风格等。相似于XPath,Selenium2也可使用CSS选择器来定位元素。
请查看以下的HTML文档。
<div class="minicart-wrapper"> <p class="block-subtitle">Recently added item(s) <a class="close skip-link-close" href="#" title="Close">×</a> </p> <p class="empty">You have no items in your shopping cart. </p> </div>
咱们来建立一个测试,验证这些消息是否正确。
def test_shopping_cart_status(self): # check content of My Shopping Cart block on Home page # get the Shopping cart icon and click to open the # Shopping Cart section shopping_cart_icon = self.driver.\ find_element_by_css_selector("div.header-minicart span.icon") shopping_cart_icon.click() # get the shopping cart status shopping_cart_status = self.driver.\ find_element_by_css_selector("p.empty").text self.assertEqual("You have no items in your shopping cart.", shopping_cart_status) # close the shopping cart section close_button = self.driver.\ find_element_by_css_selector("div.minicart-wrapper a.close") close_button.click()
特殊 iframe 操做
iframe 元素会建立包含另一个文档的内联框架(即行内框架)。
iframe: 紫禁城
在一个<html>中,包含了另外一个<html>
示例
<html> <head> <title>iframe示例</title> </head> <body> <h1> 这里是H1,标记了标题 </h1> <p> 这里是段落,标记一个段落,属于外层 </p> <div> <iframe id="iframe-1"> <html> <body> <p> 这里是个段落,属于内层,内联框架中的 </p> <div id="div-1"> <p class="hahahp"> 这里是div中的段落,须要被定位 </p> </div> </body> </html> </iframe> </div> </body> </html>
须要定位上面示例中的<p>
:这里是div中的段落,须要被定位
以下是selenium WebDiriver的代码
<select> 是选择列表
Select 是个selenium的类selenium.webdriver.support.select.Select
Select 类的路径:
C:\Python35\Lib\site-packages\selenium\webdriver\support\select.py
<select id="brand"> <option value ="volvo">Volvo</option> <option value ="saab">Saab</option> <option value="opel">Opel</option> <option value="audi">Audi</option> </select>
示例,选择 Audi
## 查找并定位到 select element_select = driver.find_element_by_css_selector('#brand') ## 用Select类的构造方法,实例化一个对象 object_select object_select = Select(element_select) ## 操做 object_select object_select.select_by_index(3) ## 也能够这样 object_select.select_by_value('audi') ## 还能够这样 object_select.select_by_visible_text('Audi')
组合操做
自动化经验的积累,须要100%按照手工的步骤进行操做。
好比步骤以下:
<a id="customer_chosen">
<ul id="customer_list">
<ul>
的第五个<li>
代码示例
driver.find_element_by_css_selector('#customer_chosen').click() sleep(1) driver.find_element_by_css_selector('#customer_list > li:nth-child(5)')
5.3 鼠标事件操做
Web测试中,有关鼠标的操做,不仅是单击,有时候还要作右击、双击、拖动等操做。这些操做包含在ActionChains类中。
经常使用的鼠标方法:
例子:
1 # 方法模拟鼠标右键,参考代码以下: 2 # 引入ActionChains 类 3 from selenium.webdriver.common.action_chains import ActionChains 4 ... 5 # 定位到要右击的元素 6 right =driver.find_element_by_xpath("xx") 7 # 对定位到的元素执行鼠标右键操做 8 ActionChains(driver).context_click(right).perform() 9 ... 10 # 定位到要双击的元素 11 double = driver.find_element_by_xpath("xxx") 12 # 对定位到的元素执行鼠标双击操做 13 ActionChains(driver).double_click(double).perform()
5.4 键盘事件操做
键盘操做常常处理的以下:
代码以下
1 from selenium import webdriver 2 3 # 引入Keys 类包 4 from selenium.webdriver.common.keys import Keys 5 import time 6 7 driver = webdriver.Chrome() 8 driver.get("http://www.baidu.com") 9 10 # 输入框输入内容 11 driver.find_element_by_id("kw").send_keys("selenium") 12 time.sleep(3) 13 14 # 删除多输入的一个m 15 driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE) 16 time.sleep(3) 17 18 # 输入空格键+“教程” 19 driver.find_element_by_id("kw").send_keys(Keys.SPACE) 20 driver.find_element_by_id("kw").send_keys("教程") 21 time.sleep(3) 22 23 # ctrl+a 全选输入框内容 24 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
5.5 截图操做
截图的方法:save_screenshot(file)
6. unittest 单元测试框
在上一节,咱们对 Selenium WebDriver 的使用,仅仅停留在让网页自动的进行操做的阶段,并无对任何一个步骤进行“检查”。固然,这样没有“检查”的操做,其实是没有测试意义的。那么第一项,咱们须要解决的即是“检查”的问题。
所谓“检查”,实际上就是断言。对须要检查的步骤操做,经过对预先设置的指望值,和执行结果的实际值之间的对比,获得测试的结果。在这里,咱们并不须要单独的写 if 语句进行各类断定,而是可使用编程语言中对应的单元测试框架,便可解决好此类问题。
目前 Java 语言主流的单元测试框架有 JUnit 和 TestNG。Python 语言主流的单元测试框架有 unittest 。本小节的内容,主要介绍 unittest 的使用,探讨单元测试框架如何帮助自动化测试。
接下来咱们将会使用 Python 语言的unittest框架展开“检查”。unittest框架的本来的名字是PyUnit。是从JUnit 这样一个被普遍使用的 经典的Java应用开发的单元测试框架创造而来。相似的框架还有NUnit(.Net开发的单元测试框架)等。咱们可使用unittest框架为任意Python项目编写可理解的单元测试集合。如今这个unittest已经做为Python的标准库模块发布。咱们安装完Python之后,即可以直接使用unittest。
使用unittest须要如下简单的三步:
unittest 并未使用 Java 语言常见的注解方式,依旧停留在 比较早期的 Java 版本中依靠方法名称进行识别的方式。主要有如下两个固定名字的方法:
具体的代码以下:
1 ## 引入unittest模组 2 import unittest 3 4 ## 定义测试类,名字为DemoTests 5 ## 该类必须继承unittest.TestCase基类 6 class DemoTests(unittest.TestCase): 7 8 ## 使用'@'修饰符,注明该方法是类的方法 9 ## setUpClass方法是在执行测试以前须要先调用的方法 10 ## 是开始测试前的初始化工做 11 @classmethod 12 def setUpClass(cls): 13 print("call setUpClass()") 14 15 ## 每个测试开始前的预置条件 16 def setUp(self): 17 print("call setUp()") 18 19 ## 每个测试结束之后的清理工做 20 def tearDown(self): 21 print("call tearDown()") 22 23 ## 测试一(务必以test开头) 24 def test_01(self): 25 print("call test_01()") 26 pass 27 28 ## 测试三(务必以test开头) 29 def test_02(self): 30 print("call test_02()") 31 pass 32 33 ## 测试三(务必以test开头) 34 def test_03(self): 35 print("call test_03()") 36 pass 37 38 ## tearDownClass方法是执行完全部测试后调用的方法 39 ## 是测试结束后的清除工做 40 @classmethod 41 def tearDownClass(cls): 42 print("call tearDownClass()") 43 44 # 执行测试主函数 45 if __name__ == '__main__': 46 ## 执行main全局方法,将会执行上述全部以test开头的测试方法 47 unittest.main(verbosity=2)
须要注意步骤:
上述代码运行结果以下:
1 call setUpClass() 2 call setUp() 3 call test_01() 4 call tearDown() 5 call setUp() 6 call test_02() 7 call tearDown() 8 call setUp() 9 call test_06() 10 call tearDown() 11 call tearDownClass()
为何选择 unittest
unittest 的断言配置使用,unittest 的断言,属于 TestCase
类,只要继承了该类,都可以经过 self调用断言
7. 为何须要封装 Selenium
封装是一个面向对象编程的概念,是面向对象编程的核心属性,经过将代码内部实现进行密封和包装,从而简化编程。对Selenium进行封装的好处主要有以下三个方面:
8. 封装的概念与基本操做
8.1 关键方法的封装思路
封装的具体示例:
找到一个指定输入框(selector),而且输入指定的字符(text)
type(selector, text)
不用在业务逻辑中,使用屡次的 find_element_by_id(...))
def type(self, selector, text): """ Operation input box. Usage: driver.type("i,el","selenium") """ el = self._locate_element(selector) el.clear() el.send_keys(text)
click(selector)
1 def click(self, selector): 2 """ 3 It can click any text / image can be clicked 4 Connection, check box, radio buttons, and even drop-down box etc.. 5 Usage: 6 driver.click("i,el") 7 """ 8 el = self._locate_element(selector) 9 el.click()
switch_to_frame(selector)
1 def switch_to_frame(self, selector): 2 """ 3 Switch to the specified frame. 4 Usage: 5 driver.switch_to_frame("i,el") 6 """ 7 el = self._locate_element(selector) 8 self.base_driver.switch_to.frame(el)
select_by_index(selector, index)
1 def select_by_index(self, selector, index): 2 """ 3 It can click any text / image can be clicked 4 Connection, check box, radio buttons, and even drop-down box etc.. 5 Usage: 6 driver.select_by_index("i,el") 7 """ 8 el = self._locate_element(selector) 9 Select(el).select_by_index(index)
以上的代码是封装了_locate_element()的几种方法,在具体使用封装过的代码的时候,只须要简单的调用便可。接下来的重点,是介绍 _locate_element(selector)的封装方式。
1 def _locate_element(self, selector): 2 """ 3 to locate element by selector 4 :arg 5 selector should be passed by an example with "i,xxx" 6 "x,//*[@id='langs']/button" 7 :returns 8 DOM element 9 """ 10 if self.by_char not in selector: 11 return self.base_driver.find_element_by_id(selector) 12 13 selector_by = selector.split(self.by_char)[0].strip() 14 selector_value = selector.split(self.by_char)[1].strip() 15 if selector_by == "i" or selector_by == 'id': 16 element = self.base_driver.find_element_by_id(selector_value) 17 elif selector_by == "n" or selector_by == 'name': 18 element = self.base_driver.find_element_by_name(selector_value) 19 elif selector_by == "c" or selector_by == 'class_name': 20 element = self.base_driver.find_element_by_class_name(selector_value) 21 elif selector_by == "l" or selector_by == 'link_text': 22 element = self.base_driver.find_element_by_link_text(selector_value) 23 elif selector_by == "p" or selector_by == 'partial_link_text': 24 element = self.base_driver.find_element_by_partial_link_text(selector_value) 25 elif selector_by == "t" or selector_by == 'tag_name': 26 element = self.base_driver.find_element_by_tag_name(selector_value) 27 elif selector_by == "x" or selector_by == 'xpath': 28 element = self.base_driver.find_element_by_xpath(selector_value) 29 elif selector_by == "s" or selector_by == 'css_selector': 30 element = self.base_driver.find_element_by_css_selector(selector_value) 31 else: 32 raise NameError("Please enter a valid type of targeting elements.") 33 34 return element
8.2 面向对象编程思想的运用
8.3 封装后的方法如何被调用
使用上面的封装类,就须要指定特定的 selector
具体调用示例
1 def login(self, account, password, keep): 2 """ 3 登陆系统 4 :param account: 5 :param password: 6 :param keep: 7 :return: 返回保持登陆复选框的 checked 值 8 """ 9 self.base_driver.type(self.LOGIN_ACCOUNT_SELECTOR, account) 10 self.base_driver.type(self.LOGIN_PASSWORD_SELECTOR, password) 11 12 current_checked = self.get_current_keep_value() 13 if keep: 14 if current_checked is None: 15 self.base_driver.click(self.LOGIN_KEEP_SELECTOR) 16 else: 17 if current_checked == "true": 18 self.base_driver.click(self.LOGIN_KEEP_SELECTOR) 19 20 actual_checked = self.get_current_keep_value() 21 self.base_driver.click(self.LOGIN_SUBMIT_SELECTOR) 22 sleep(2) 23 return actual_checked
9.1 Page-Object设计模式的本质
Page Object设计模式是Selenium自动化测试项目的最佳设计模式之一,强调测试、逻辑、数据和驱动相互分离。
Page Object模式是Selenium中的一种测试设计模式,主要是将每个页面设计为一个Class,其中包含页面中须要测试的元素(按钮,输入框,标题等),这样在Selenium测试页面中能够经过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,须要改测试页面代码的状况。当页面元素id变化时,只须要更改测试页Class中页面的属性便可。
它的好处以下:
具体的作法以下:
9.2 Page 如何划分
通常经过继承的方式,进行按照实际Web页面进行划分
9.3 Page-Object 类如何实现
实现的示例
传递 driver的构造方法
1 LOGIN_ACCOUNT_SELECTOR = "s, #account" 2 LOGIN_PASSWORD_SELECTOR = "s, #password" 3 LOGIN_KEEP_SELECTOR = "s, #keepLoginon" 4 LOGIN_SUBMIT_SELECTOR = "s, #submit" 5 LOGIN_LANGUAGE_BUTTON_SELECTOR = "s, #langs > button" 6 LOGIN_LANGUAGE_MENU_SELECTOR = "s, #langs > ul > li:nth-child(%d) > a" 7 LOGIN_FAIL_MESSAGE_SELECTOR = "s, body > div.bootbox.modal.fade.bootbox-alert.in > div > div > div.modal-body"
每一个子类都须要的系统功能:
open
具体的页面的类,定义了某个具体的页面的功能
必须继承基类
self.base_driver
成员变量Tests 类
这部分描述的是具体的测试用例。
声明全局变量
10.构建测试方案
10.1 数据驱动在自动化测试中的应用
(1)什么是数据驱动?
主要的数据驱动方式有两种:
经过 CSV 文件 或者 MySQL 数据库,是主流的数据驱动方式。固然数据驱动也能够结合单元测试框架的参数化测试进行编写(此部分本文不作具体描述)。
不管使用了 哪种(CSV 或者 MySQL),读取数据后都要进行遍历操做。
(2)使用 csv
1 import csv 2 3 csv_file = open("xxx.csv", "r", encoding="utf8") 4 csv_data = csv.reader(csv_file) 5 for row in csv_data: 6 # 进行测试 7 # 使用字典类型 8 data_to_test = { 9 "key1": row[0], 10 "key2": row[1] 11 } 12 13 csv_file.close()
(3)使用 MySQL
import pymysql connect = pymysql.connect(host="xx", port=3306, user="root", passwd="xxx", db="xx") cur = connect.cursor() cur.execute("SELECT...") mysql_data = cur.fetchall() for row in mysql_data: # 进行测试 # 使用字典类型 data_to_test = { "key1": row[0], "key2": row[1] } cur.close() connect.close()
(4)须要掌握的知识点:
dict
类型
10.2 测试方案的编码实现
.
10.3 测试报告的生成
测试的示例代码以下
1 # 声明一个测试套件 2 suite = unittest.TestSuite() 3 # 添加测试用例到测试套件 4 suite.addTest(RanzhiTests("test_ranzhi_login")) 5 6 # 建立一个新的测试结果文件 7 buf = open("./result.html", "wb") 8 9 # 声明测试运行的对象 10 runner = HTMLTestRunner.HTMLTestRunner(stream=buf, 11 title="Ranzhi Test Result", 12 description="Test Case Run Result") 13 # 运行测试,而且将结果生成为HTML 14 runner.run(suite) 15 16 # 关闭文件输出 17 buf.close()