写在最前面:目前自动化测试并不属于新鲜的事物,或者说自动化测试的各类方法论已经层出不穷,可是,可以在项目中锲而不舍的实践自动化测试的团队,却依旧不是很是多。有的团队知道怎么作,作的还不够好;有的团队还正在探索和摸索怎么作,甚至还有一些多方面的技术上和非技术上的旧系统须要重构……css
本文将会从
使用
和实践
两个视角,尝试对基于Web UI
自动化测试作细致的分析和解读,给各位去思考和实践作一点引路,以便各团队能找到更好的方式。html
《论语》有云:工欲善其事,必先利其器。在开始具体的自动化测试以前,咱们须要作好更多的准备,包括如下几个方面:前端
认识自动化测试node
准备自动化测试工具python
使用有效的方式mysql
针对具体的测试对象git
接下来的第一部份内容,咱们将会从上述的几个方面进行探讨。github
自动化测试的5W
web
正如开篇所提到的,自动化测试再也不是一个陌生的话题,而是一个具体的存在。做为测试实践活动的一部分,咱们首先分析一下自动化测试的方方面面。sql
WHAT
, 什么是自动化测试
G.J.Myers在其经典的著做《软件测试艺术》(The Art of Software Testing)一书中,给出了测试的定义:
“程序测试是为了发现错误而执行的过程。”
这个概念产生于30年前,对软件测试的认识还很是有局限性,固然也是由于受瀑布开发模型的影响,认为软件测试是编程以后的一个阶段。只有等待代码开发出来之后,经过执行程序,像用户那样操做软件去发现问题。这里向你们推荐一个测试交流圈q裙:1007119548。
自动化测试:以人为驱动的测试行为转化为机器执行的一种过程
自动化测试,就是把手工进行的测试过程,转变成机器自动执行的测试过程。该过程,依旧是为了发现错误而执行。所以自动化测试的关键在于“自动化”三个字。自动化测试的内容,也就相应的转变成如何“自动化”去实现本来手工进行的测试的过程。
全部的“自动化”,依靠的无疑都是程序。
经过程序,能够把手工测试,转变成自动化测试。
WHEN
, 在何时开展自动化测试
自动化测试的开展,依赖于“程序”。那么程序,其实就是由“源代码”构建而来的。那么原则上,只要能作出自动化测试所须要的“程序”的时候,变能够进行自动化测试。但每每,并非全部的“时候”都是好的“时机”。从这个W
开始,咱们将会加入对于成本的顾虑,也正是由于“成本”的存在,才使得下面的讨论,变得有意义。
全部的开销,都是有成本的。构建成“程序”的源代码,也是由工程师写出来的。那么须要考虑这个过程当中的成本。基于这个考虑,在可以比较稳定的构建“程序”的时候,不须要花费太多开销在“源代码”的时候,就是开展自动化测试的好时机。这个开销包括编写
和修改
源代码,而源代码指的是构建出用来作自动化测试的程序
的源代码。
WHERE
, 在什么地方进行自动化测试
自动化测试的执行,依靠的是机器。那么自动化测试必将在“机器”上进行。通常来讲,这个机器包括桌面电脑和服务器。经过将写好的源代码
部署在机器上,构建出用来作自动化测试的"程序",而且运行该程序,实现自动化测试。
WHICH
, 对什么目标进行自动化测试
自动化测试的目标,是被测试的软件。抛开人工智能的成分,手工测试必将在“人工智能”足够普及和足够“智能”以前,替代一大部分不须要“人类智能”的手工测试;以及自动化测试会作一些手工测试没法实施的,或者手工测试没法覆盖的测试。
HOW
, 如何开展自动化测试
和全部的其余测试同样,自动化测试的流程也是由“用例”执行和“缺陷”验证组成。差异是须要找到合适的“工具”来替代“人手”。不一样目标的自动化测试有不一样的测试工具,可是任何工具都无不例外的须要“编程”的过程,实现“源代码”,也能够称之为测试脚本。因而开展自动化测试的方式基本上以下:
自动化测试的典型金字塔原理
谈到自动化测试,就不得不提的一我的和概念就是:Martin Fowler和他的金字塔原理。首先请看金字塔原理的图示以下:
该图说明了三个问题:
这是理想中的金字塔原理。
在实际的项目中,尤为是结合国内的项目实践,其实还隐藏了另外一个问题:越是顶层的测试,效果越明显。有句话说“贵的东西,除了贵,其余都是好的!”可以很清晰的阐述这个观点。
金字塔原理在国内的适应性也有必定的问题
相对来讲,在基于UI前端界面的自动化测试反却是开展和实施的不是特别多。尽管基于界面的测试带来的效果仍是可以立竿见影的。对于产品的质量提高,仍是比较容易有保证。
自动化测试的适用范围
自动化测试能够涉及和试用的范围主要在如下方面:
Web UI
的浏览器应用的界面测试WebService
或者WebAPI
的服务契约测试WCF
、.net remoting
、Spring
等框架的服务的集成测试APP UI
的移动应用界面测试Java
、C#
等编程文件进行的单元测试本文集中讨论第一条:基于Web UI
的浏览器应用的界面测试。界面的改动对于测试来讲,具备较大的成本风险。主要考虑如下方面:
自动化测试的流程
自动化测试和普通的手工测试遵循的测试流程,与项目的具体实践相关。通常来讲,也是须要从测试计划开始涉及自动化测试的。
基于Web UI
的自动化测试工具主要有两大类:付费的商业版工具和无偿使用的开源版工具。典型的有两种:
1.2.1 Selenium 基本介绍
Selenium`是开源的自动化测试工具,它主要是用于Web 应用程序的自动化测试,不仅局限于此,同时支持全部基于web 的管理任务自动化。
Selenium
官网的介绍
Selenium is a suite of tools to automate web browsers across many platforms.
- runs in many browsers and operating systems
- can be controlled by many programming languages and testing frameworks.
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 工具集
Selenium IDE
Selenium IDE (集成开发环境) 是一个建立测试脚本的原型工具。它是一个 Firefox 插件,实现简单的浏览器操做的录制与回放功能,提供建立自动化测试的建议接口。Selenium IDE 有一个记录功能,能记录用户的操做,而且能选择多种语言把它们导出到一个可重用的脚本中用于后续执行。这里向你们推荐一个测试交流圈q裙:1007119548。
Selenium RC
Selenium RC 是selenium 家族的核心工具,Selenium RC 支持多种不一样的语言编写自动化测试脚本,经过selenium RC 的服务器做为代理服务器去访问应用从而达到测试的目的。
selenium RC 使用分Client Libraries 和Selenium Server。
Selenium Grid
Selenium Grid 使得 Selenium RC 解决方案能提高针对大型的测试套件或者哪些须要运行在多环境的测试套件的处理能力。Selenium Grid 能让你并行的运行你的测试,也就是说,不一样的测试能够同时跑在不一样的远程机器上。这样作有两个有事,首先,若是你有一个大型的测试套件,或者一个跑的很慢的测试套件,你可使用 Selenium Grid 将你的测试套件划分红几份同时在几个不一样的机器上运行,这样能显著的提高它的性能。同时,若是你必须在多环境中运行你的测试套件,你能够得到多个远程机器的支持,它们将同时运行你的测试套件。在每种状况下,Selenium Grid 都能经过并行处理显著地缩短你的测试套件的处理时间。
Selenium WebDriver
WebDriver 是 Selenium 2 主推的工具,事实上WebDriver是Selenium RC的替代品,由于Selenium须要保留向下兼容性的缘由,在 Selenium 2 中, Selenium RC才没有被完全的抛弃,若是使用Selenium开发一个新的自动化测试项目,那么咱们强烈推荐使用Selenium2 的 WebDriver进行编码。另外, 在Selenium 3 中,Selenium RC 被移除了。
Python 语言的选择,便捷
/'paɪθən/
使用的工具集
1.2.2 JetBrains PyCharm 使用
JetBrains PyCharm 的介绍
PyCharm 是 JetBrains 公司针对Python推出的IDE(Integrated Development Environment,集成开发环境)。是目前最好的Python IDE之一。目前包含了两个版本:
咱们推荐使用免费的社区版本,进行Python脚本的编写和自动化测试执行。
PyCharm能够在官网下载,www.jetbrains.com
PyCharm 安装后,若是也安装过 Python 环境,能够直接进行操做。不然请在 1.2.3 中安装好 Python,再使用 PyCharm。
安装按照默认的步骤安装
使用方式
Create New Project:
建立新的项目,选择项目建立的位置,选择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(建议选择,等宽字体)
SVN / Git 在工具中的集成
源代码管理工具(VCS, version control system)
若是TortoiseSVN版本低于
1.8
,须要先升级安装1.8
以上的版本
选择SVN(git)做为代码的源代码管理工具。集成在PyCharm中的步骤以下
D:\SVN\XXProject\Trunck
2. 代码没有建立:在本地的SVN项目文件夹中新建项目,用PyCharm打开,提交。
复制代码
3. 用PyCharm打开 刚刚部署的代码
4. 选择PyCharm的 `VCS`|`Enable VCS integration`,选择 Subversion(svn) 或者 Git
复制代码
5. 右键项目文件的根目录,选择 Subversion | add to VCS
复制代码
6. 右键项目文件的根目录,或者选 VCS | Commit Directory...
复制代码
天天打开代码后,右键项目文件的根目录,首先 Subversion | update project
若是有冲突,先本地手工保存你作的修改(备份你的文件到其余地方,SVN目录以外的地方,而后Revert)
1.2.3 Selenium 的环境搭建
在 Windows 搭建和部署 Selenium 工具
主要包括两个步骤:
安装 Python 语言
Python的官方网站: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
安装过程当中不要关闭弹出来的命令行窗口
关于 Python 的安装,也能够选择一些第三方的Python 安装包,典型的有 Anaconda3
,这样的包有丰富的第三方库,在使用 Python 的过程当中会更加方便。这里向你们推荐一个测试交流圈q裙:1007119548。
Anaconda 的官网:www.continuum.io/anaconda-ov…
安装 Selenium 工具包
因为 安装好的 Python 默认有 pip
Python 包管理工具,能够经过 pip
很是方便的安装 Selenium。
启动命令行工具:Win+R | 输入 cmd | 回车
输入命令:
pip install selenium
复制代码
该命令的执行须要有互联网联网环境。此外该命令有如下几种选项可使用
安装指定的版本,例如安装指定的 Selenium 3.4.3
pip install selenium==3.4.3
复制代码
安装最新版的 Selenium
pip install -U selenium
# -U 也能够用 --upgrade
pip install --upgrade selenium
复制代码
复制代码
pip uninstall selenium
复制代码
固然,若是您的机器处于非接入互联网的环境,您能够事先下载 Selenium 的 Python 安装包,再进行手动安装。
官方下载地址:pypi.python.org/pypi/seleni…
上述地址会下载最新版的 Selenium,目前最早版的是 3.4.3,您也能够根据如下路径下载指定的 3.4.3
Selenium 3.4.3 下载地址:pypi.python.org/pypi/seleni…
下载后,解压该压缩包
而后用命令行进入该压缩包的根目录,输入命令进行安装
python setup.py install
复制代码
配置 浏览器 和 驱动
Selenium 2 能够默认支持Firefox 46.0或者更低版本,对于其余浏览器须要额外安装驱动。
Selenium 3 对于全部的浏览器都须要安装驱动,本文以 Chrome 和 Firefox、IE为例设置浏览器和驱动。
ChromeDriver下载地址:chromedriver.storage.googleapis.com/index.html
ChromeDriver 与 Chrome 对应关系表:
ChromeDriver版本 | 支持的Chrome版本 |
---|---|
v2.31 | v58-60 |
v2.30 | v58-60 |
v2.29 | v56-58 |
v2.28 | v55-57 |
v2.27 | v54-56 |
v2.26 | v53-55 |
v2.25 | v53-55 |
v2.24 | v52-54 |
v2.23 | v51-53 |
v2.22 | v49-52 |
v2.21 | v46-50 |
v2.20 | v43-48 |
GeckoDriver下载地址:github.com/mozilla/gec…
GeckoDriver 与 Firefox 的对应关系表:
GeckoDriver版本 | 支持的Firefox版本 |
---|---|
v0.18.0 | v56 |
v0.17.0 | v55 |
v0.16.0 | v54,须要Selenium 3.4或者以上 |
v0.15.0 | v53,须要Selenium 3.3或者以上 |
IEDriverServer下载地址:selenium-release.storage.googleapis.com/index.html
IEDriverServer 的版本须要与 Selenium 保持严格一致。
浏览器驱动的配置
经过上一节的环境安装成功之后,咱们能够进行第一个对Selenium 的使用,就是最简脚本编写。脚本以下:
# 声明一个司机,司机是个Chrome类的对象
driver = webdriver.Chrome()
# 让司机加载一个网页
driver.get("http://demo.ranzhi.org")
# 给司机3秒钟去打开
sleep(3)
# 开始登陆
# 1\. 让司机找用户名的输入框
we_account = driver.find_element_by_css_selector('#account')
we_account.clear()
we_account.send_keys("demo")
# 2\. 让司机找密码的输入框
we_password = driver.find_element_by_css_selector('#password')
we_password.clear()
we_password.send_keys("demo")
# 3\. 让司机找 登陆按钮 并 单击
driver.find_element_by_css_selector('#submit').click()
sleep(3)
复制代码
实际上一段20行的代码,也不能算太少了。可是这段代码的使用,确实体现了 Selenium 的最简单的使用。咱们在下面内容进行阐述。
关于面向对象编程
经过前面的介绍,咱们知道 Selenium 支持多种语言,而且推荐使用面向对象的方式进行编程。接下来咱们将着重介绍如何使用面向对象的方式进行编程。
咱们利用 Python 进行面向对象编程,须要首先了解一个概念:类
类
类是任何面向对象编程的语言的基本组成,描述了使用的基本方法。咱们可能在目前,还不是特别明白类的含义,可是咱们能够经过类的使用,来进一步了解。
类的使用
类,经过实例化进行使用。好比有一个类: Driver
,该类有一个方法: head(road)
那么关于这个类的使用,只须要两个步骤:
d = Driver()
d.head("中山路")
了解上述例子和使用之后,咱们来看具体的 Selenium 的使用。
这里向你们推荐一个测试交流圈q裙:1007119548。
具体的对象的使用
在面向对象的理念看来,任何的编码,都是由对象而来的,这里也不例外。和以前介绍 WebDriver 时候的描述对应,咱们须要用到两种主要的类,并将其实例化。
上述代码中,使用了一个 WebDriver 类 的对象,即第2行,声明了该类的对象,并赋值给变量 driver,接着变量 driver 做为 WebDriver 类的对象,使用了多个 WebDriver 类的方法。
注意:Chrome 是 WebDriver 的子类,是 WebDriver 类的一种
we_account
,we_password
和最后一个匿名的对象,并经过产生的三个对象,调用 WebElement 类的方法
正是经过这样的面向对象的方式,产生 Web司机(WebDriver类的对象),而且经过 Web司机不懈的努力,寻找到各类 Web元素(WebElement类的对象)进行操做,这样便实现了 Selenium WebDriver 做为一款出色的浏览器测试工具,进行浏览器UI界面的自动化测试的代码编写和用例执行。
经过上述最简脚本的使用,咱们能够来进一步了解 Selenium 的使用。事实上,上一节用的,即是 Selenium 的 WebDriver API。API(Application Programming Interface,应用程序编程接口,即经过编程语言,操做 WebDriver 的方法集合)
Selenium WebDriver API 官方参考:seleniumhq.github.io/selenium/do…
具体API文档地址:seleniumhq.github.io/selenium/do…
1.4.1 控制浏览器
浏览器的控制也是自动化测试的一个基本组成部分,咱们能够将浏览器最大化,设置浏览器的高度和宽度以及对浏览器进行导航操做等。
# 浏览器打开网址
driver.get("https://www.baidu.com")
# 浏览器最大化
driver.maximize_window()
# 设置浏览器的高度为800像素,宽度为480像素
driver.set_window_size(480, 800)
# 浏览器后退
driver.back()
# 浏览器前进
driver.forward()
# 浏览器关闭
driver.close()
# 浏览器退出
driver.quit()
复制代码
1.4.2 元素定位操做
WebDriver提供了一系列的定位符以便使用元素定位方法。常见的定位符有如下几种:
那么咱们如下的操做将会基于上述的定位符进行定位操做。
对于元素的定位,WebDriver API能够经过定位简单的元素和一组元素来操做。在这里,咱们须要告诉Selenium如何去找元素,以致于他能够充分的模拟用户行为,或者经过查看元素的属性和状态,以便咱们执行一系列的检查。
在Selenium2中,WebDriver提供了多种多样的find_element_by
方法在一个网页里面查找元素。这些方法经过提供过滤标准来定位元素。固然WebDriver也提供了一样多种多样的find_elements_by
的方式去定位多个元素。
尽管上述的方式,能够进行元素定位,实际上咱们也是更多的用组合的方式进行元素定位。
方法Method | 描述Description | 参数Argument | 示例Example |
---|---|---|---|
id |
该方法经过ID的属性值去定位查找单个元素 | id: 须要被查找的元素的ID | find_element_by_id('search') |
name |
该方法经过name的属性值去定位查找单个元素 | name: 须要被查找的元素的名称 | find_element_by_name('q') |
class name |
该方法经过class的名称值去定位查找单个元素 | class_name: 须要被查找的元素的类名 | find_element_by_class_name('input-text') |
tag_name |
该方法经过tag的名称值去定位查找单个元素 | tag: 须要被查找的元素的标签名称 | find_element_by_tag_name('input') |
link_text |
该方法经过连接文字去定位查找单个元素 | link_text: 须要被查找的元素的连接文字 | find_element_by_link_text('Log In') |
partial_link_text |
该方法经过部分连接文字去定位查找单个元素 | link_text: 须要被查找的元素的部分连接文字 | find_element_by_partial_link_text('Long') |
xpath |
该方法经过XPath的值去定位查找单个元素 | xpath: 须要被查找的元素的xpath | find_element_by_xpath('//*[@id="xx"]/a') |
css_selector |
该方法经过CSS选择器去定位查找单个元素 | css_selector: 须要被查找的元素的ID | find_element_by_css_selector('#search') |
接下来的列表将会详细展现find_elements_by
的方法集合。这些方法依据匹配的具体标准返回一系列的元素。
方法Method | 描述Description | 参数Argument | 示例Example |
---|---|---|---|
id |
该方法经过ID的属性值去定位查找多个元素 | id: 须要被查找的元素的ID | find_elements_by_id('search') |
name |
该方法经过name的属性值去定位查找多个元素 | name: 须要被查找的元素的名称 | find_elements_by_name('q') |
class_name |
该方法经过class的名称值去定位查找多个元素 | class_name: 须要被查找的元素的类名 | find_elements_by_class_name('input-text') |
tag_name |
该方法经过tag的名称值去定位查找多个元素 | tag: 须要被查找的元素的标签名称 | find_elements_by_tag_name('input') |
link_text |
该方法经过连接文字去定位查找多个元素 | link_text: 须要被查找的元素的连接文字 | find_elements_by_link_text('Log In') |
partial_link_text |
该方法经过部分连接文字去定位查找多个元素 | link_text: 须要被查找的元素的部分连接文字 | find_elements_by_partial_link_text('Long') |
xpath |
该方法经过XPath的值去定位查找多个元素 | xpath: 须要被查找的元素的xpath | find_elements_by_xpath("//div[contains(@class,'list')]") |
css_selector |
该方法经过CSS选择器去定位查找多个元素 | css_selector: 须要被查找的元素的ID | find_element_by_css_selector('.input_class') |
依据ID查找
请查看以下HTML的代码,以便实现经过ID的属性值去定义一个查找文本框的查找:
<input id="search" type="text" name="q" value=""
class="input-text" maxlength="128" autocomplete="off"/>
复制代码
根据上述代码,这里咱们使用find_element_by_id()
的方法去查找搜索框而且检查它的最大长度maxlength
属性。咱们经过传递ID的属性值做为参数去查找,参考以下的代码示例:
def test_search_text_field_max_length(self):
# get the search textbox
search_field = self.driver.find_element_by_id("search")
# check maxlength attribute is set to 128
self.assertEqual("128", search_field.get_attribute("maxlength"))
复制代码
若是使用find_elements_by_id()
方法,将会返回全部的具备相同ID属性值的一系列元素。
依据名称name查找
这里仍是根据上述ID查找的HTML代码,使用find_element_by_name
的方法进行查找。参考以下的代码示例:
# get the search textbox
self.search_field = self.driver.find_element_by_name("q")
复制代码
一样,若是使用find_elements_by_name()
方法,将会返回全部的具备相同name属性值的一系列元素。
依据class name查找
除了上述的ID和name的方式查找,咱们还可使用class name的方式进行查找和定位。
事实上,经过ID,name或者类名class name查找元素是最提倡推荐的和最快的方式。固然Selenium2 WebDriver也提供了一些其余的方式,在上述三类方式条件不足,查找无效的时候,能够经过这些其余方式来查找。这些方式将会在后续的内容中讲述。
请查看以下的HTML代码,经过改代码进行练习和理解.
<button type="submit" title="Search" class="button">
<span><span>Search</span></span>
</button>
复制代码
根据上述代码,使用find_element_by_class_name()
方法去定位元素。
def test_search_button_enabled(self):
# get Search button
search_button = self.driver.find_element_by_class_name("button")
# check Search button is enabled
self.assertTrue(search_button.is_enabled())
复制代码
一样的若是使用find_elements_by_class_name()
方法去定位元素,将会返回全部的具备相同name属性值的一系列元素。
依据标签名tag name查找
利用标签的方法相似于利用类名等方法进行查找。咱们能够轻松的查找出一系列的具备相同标签名的元素。例如咱们能够经过查找表中的<tr>
来获取行数。
下面有一个HTML的示例,这里在无序列表中使用了<img>
标签。
<ul class="promos">
<li>
<a href="http://demo.magentocommerce.com/home-decor.html">
<img src="/media/wysiwyg/homepage-three-column-promo- 01B.png" alt="Physical & Virtual Gift Cards">
</a>
</li>
<li>
<a href="http://demo.magentocommerce.com/vip.html">
<img src="/media/wysiwyg/homepage-three-column-promo- 02.png" alt="Shop Private Sales - Members Only">
</a>
</li>
<li>
<a href="http://demo.magentocommerce.com/accessories/ bags-luggage.html">
<img src="/media/wysiwyg/homepage-three-columnpromo- 03.png" alt="Travel Gear for Every Occasion">
</a>
</li>
</ul>
复制代码
这里面咱们使用find_elements_by_tag_name()
的方式去获取所有的图片,在此以前,咱们将会使用find_element_by_class_name()
去获取到指定的<ul>
。
具体代码以下:
def test_count_of_promo_banners_images(self):
# get promo banner list
banner_list = self.driver.find_element_by_class_name("promos")
# get images from the banner_list
banners = banner_list.find_elements_by_tag_name("img")
# check there are 20 tags displayed on the page
self.assertEqual(20, len(banners))
复制代码
依据连接文字link查找
连接文字查找一般比较简单。使用find_element_by_link_text
请查看如下示例
<a href="#header-account" class="skip-link skip-account">
<span class="icon"></span>
<span class="label">ACCOUNT Description</span>
</a>
复制代码
测试代码以下:
def test_my_account_link_is_displayed(self):
# get the Account link
account_link =
self.driver.find_element_by_link_text("ACCOUNT Description")
# check My Account link is displayed/visible in
# the Home page footer
self.assertTrue(account_link.is_displayed())
复制代码
依据部分连接文字partial text查找
这里依旧使用上述的列子进行代码编写:
def test_account_links(self):
# get the all the links with Account text in it
account_links = self.driver.\
find_elements_by_partial_link_text("ACCOUNT")
# check Account and My Account link is
# displayed/visible in the Home page footer
self.assertTrue(2, len(account_links))
复制代码
依据XPath进行查找
XPath是一种在XML文档中搜索和定位节点node的一种查询语言。全部的主流Web浏览器都支持XPath。Selenium2能够用强大的XPath在页面中查找元素。
经常使用的XPath的方法有starts-with()
,contains()
和ends-with()
等
若想要了解更多关于XPath的内容,请查看www.w3schools.com/XPath/
以下有一段HTML代码,其中里面的<img>
没有使用ID,name或者类属性,因此咱们没法使用以前的方法。亚这里咱们能够经过<img>
的alt
属性,定位到指定的tag。
<ul class="promos">
<li>
<a href="http://demo.magentocommerce.com/home-decor.html">
<img src="/media/wysiwyg/homepage-three-column-promo- 01B.png" alt="Physical & Virtual Gift Cards">
</a>
</li>
<li>
<a href="http://demo.magentocommerce.com/vip.html">
<img src="/media/wysiwyg/homepage-three-column-promo- 02.png" alt="Shop Private Sales - Members Only">
</a>
</li>
<li>
<a href="http://demo.magentocommerce.com/accessories/ bags-luggage.html">
<img src="/media/wysiwyg/homepage-three-columnpromo- 03.png" alt="Travel Gear for Every Occasion">
</a>
</li>
</ul>
复制代码
具体代码以下:
def test_vip_promo(self):
# get vip promo image
vip_promo = self.driver.\
find_element_by_xpath("//img[@alt='Shop Private Sales - Members Only']")
# check vip promo logo is displayed on home page
self.assertTrue(vip_promo.is_displayed())
# click on vip promo images to open the page
vip_promo.click()
# check page title
self.assertEqual("VIP", self.driver.title)
复制代码
固然,若是使用find_elements_by_xpath()
的方法,将会返回全部匹配了XPath查询的元素。
依据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的代码
## 查找并定位 iframe
element_frame = driver.find_element_by_css_selector('#iframe-1')
## 切换到刚刚查找到的 iframe
driver.switch_to.frame(element_frame)
## 定位 <p>
driver.find_element_by_css_selector('#div-1 > p')
## TODO....
## 退出刚刚切换进去的 iframe
driver.switch_to.default_content()
复制代码
特殊 Select 操做
<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)')
复制代码
1.4.3 鼠标事件操做
Web测试中,有关鼠标的操做,不仅是单击,有时候还要作右击、双击、拖动等操做。这些操做包含在ActionChains类中。
经常使用的鼠标方法:
例子:
# 方法模拟鼠标右键,参考代码以下:
# 引入ActionChains 类
from selenium.webdriver.common.action_chains import ActionChains
...
# 定位到要右击的元素
right =driver.find_element_by_xpath("xx")
# 对定位到的元素执行鼠标右键操做
ActionChains(driver).context_click(right).perform()
...
# 定位到要双击的元素
double = driver.find_element_by_xpath("xxx")
# 对定位到的元素执行鼠标双击操做
ActionChains(driver).double_click(double).perform()
复制代码
1.4.4 键盘事件操做
键盘操做常常处理的以下:
代码 | 描述 |
---|---|
send_keys(Keys.BACKSPACE) |
删除键(BackSpace) |
send_keys(Keys.SPACE) |
空格键(Space) |
send_keys(Keys.TAB) |
制表键(Tab) |
send_keys(Keys.ESCAPE) |
回退键(Esc) |
send_keys(Keys.ENTER) |
回车键(Enter) |
send_keys(Keys.CONTROL,'a') |
全选(Ctrl+A) |
send_keys(Keys.CONTROL,'c') |
复制(Ctrl+C) |
代码以下
from selenium import webdriver
# 引入Keys 类包
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)
# 删除多输入的一个m
driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)
time.sleep(3)
# 输入空格键+“教程”
driver.find_element_by_id("kw").send_keys(Keys.SPACE)
driver.find_element_by_id("kw").send_keys("教程")
time.sleep(3)
# ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
复制代码
1.4.5 截图操做
截图的方法:save_screenshot(file)
在上一节,咱们对 Selenium WebDriver 的使用,仅仅停留在让网页自动的进行操做的阶段,并无对任何一个步骤进行“检查”。固然,这样没有“检查”的操做,其实是没有测试意义的。那么第一项,咱们须要解决的即是“检查”的问题。
所谓“检查”,实际上就是断言。对须要检查的步骤操做,经过对预先设置的指望值,和执行结果的实际值之间的对比,获得测试的结果。在这里,咱们并不须要单独的写 if
语句进行各类断定,而是可使用编程语言中对应的单元测试框架,便可解决好此类问题。
目前 Java 语言主流的单元测试框架有 JUnit 和 TestNG。Python 语言主流的单元测试框架有 unittest 。本小节的内容,主要介绍 unittest 的使用,探讨单元测试框架如何帮助自动化测试。
接下来咱们将会使用 Python 语言的unittest
框架展开“检查”。unittest
框架的本来的名字是PyUnit。是从JUnit 这样一个被普遍使用的 经典的Java应用开发的单元测试框架创造而来。相似的框架还有NUnit(.Net开发的单元测试框架)等。咱们可使用unittest框架为任意Python项目编写可理解的单元测试集合。如今这个unittest已经做为Python的标准库模块发布。咱们安装完Python之后,即可以直接使用unittest。
使用unittest须要如下简单的三步:
test
开头unittest 并未使用 Java 语言常见的注解方式,依旧停留在 比较早期的 Java 版本中依靠方法名称进行识别的方式。主要有如下两个固定名字的方法:
具体的代码以下:
## 引入unittest模组
import unittest
## 定义测试类,名字为DemoTests
## 该类必须继承unittest.TestCase基类
class DemoTests(unittest.TestCase):
## 使用'@'修饰符,注明该方法是类的方法
## setUpClass方法是在执行测试以前须要先调用的方法
## 是开始测试前的初始化工做
@classmethod
def setUpClass(cls):
print("call setUpClass()")
## 每个测试开始前的预置条件
def setUp(self):
print("call setUp()")
## 每个测试结束之后的清理工做
def tearDown(self):
print("call tearDown()")
## 测试一(务必以test开头)
def test_01(self):
print("call test_01()")
pass
## 测试三(务必以test开头)
def test_02(self):
print("call test_02()")
pass
## 测试三(务必以test开头)
def test_03(self):
print("call test_03()")
pass
## tearDownClass方法是执行完全部测试后调用的方法
## 是测试结束后的清除工做
@classmethod
def tearDownClass(cls):
print("call tearDownClass()")
# 执行测试主函数
if __name__ == '__main__':
## 执行main全局方法,将会执行上述全部以test开头的测试方法
unittest.main(verbosity=2)
复制代码
须要注意步骤:
上述代码运行结果以下:
call setUpClass()
call setUp()
call test_01()
call tearDown()
call setUp()
call test_02()
call tearDown()
call setUp()
call test_06()
call tearDown()
call tearDownClass()
复制代码
为何选择 unittest
unittest 的断言配置使用
unittest 的断言,属于 TestCase
类,只要继承了该类,都可以经过 self调用断言
方法 Method | 检查条件 |
---|---|
assertEqual(a, b [, msg]) |
a == b,msg可选,用来解释失败的缘由 |
assertNotEqual(a, b [, msg] |
a != b,msg可选,用来解释失败的缘由 |
assertTrue(x [, msg]) |
x 是真,msg可选,用来解释失败的缘由 |
assertFalse(x [, msg]) |
x 是假,msg可选,用来解释失败的缘由 |
assertIsNot(a, b [, msg]) |
a 不是 b,msg可选,用来解释失败的缘由 |
什么是封装
封装是一个面向对象编程的概念,是面向对象编程的核心属性,经过将代码内部实现进行密封和包装,从而简化编程。对Selenium进行封装的好处主要有以下三个方面:
关键方法的封装思路
封装的具体示例:
找到一个指定输入框(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)
复制代码
找到一个能够点击的元素(selector),而且点击(click)
click(selector)
def click(self, selector):
""" It can click any text / image can be clicked Connection, check box, radio buttons, and even drop-down box etc.. Usage: driver.click("i,el") """
el = self._locate_element(selector)
el.click()
复制代码
switch_to_frame(selector)
def switch_to_frame(self, selector):
""" Switch to the specified frame. Usage: driver.switch_to_frame("i,el") """
el = self._locate_element(selector)
self.base_driver.switch_to.frame(el)
复制代码
select_by_index(selector, index)
def select_by_index(self, selector, index):
""" It can click any text / image can be clicked Connection, check box, radio buttons, and even drop-down box etc.. Usage: driver.select_by_index("i,el") """
el = self._locate_element(selector)
Select(el).select_by_index(index)
复制代码
以上的代码是封装了_locate_element()
的几种方法,在具体使用封装过的代码的时候,只须要简单的调用便可。接下来的重点,是介绍 _locate_element(selector)
的封装方式。
find_element_by_...)
selector
显示出分类selector
中须要包含一个特殊符号硬编码 hard code
来实例化,例如 ,
或者 ?
或者 其余很是用字符 =>
this.byChar
要把查找到元素的返回给调用的地方:必需要有返回值,类型是 WebElement
def _locate_element(self, selector):
""" to locate element by selector :arg selector should be passed by an example with "i,xxx" "x,//*[@id='langs']/button" :returns DOM element """
if self.by_char not in selector:
return self.base_driver.find_element_by_id(selector)
selector_by = selector.split(self.by_char)[0].strip()
selector_value = selector.split(self.by_char)[1].strip()
if selector_by == "i" or selector_by == 'id':
element = self.base_driver.find_element_by_id(selector_value)
elif selector_by == "n" or selector_by == 'name':
element = self.base_driver.find_element_by_name(selector_value)
elif selector_by == "c" or selector_by == 'class_name':
element = self.base_driver.find_element_by_class_name(selector_value)
elif selector_by == "l" or selector_by == 'link_text':
element = self.base_driver.find_element_by_link_text(selector_value)
elif selector_by == "p" or selector_by == 'partial_link_text':
element = self.base_driver.find_element_by_partial_link_text(selector_value)
elif selector_by == "t" or selector_by == 'tag_name':
element = self.base_driver.find_element_by_tag_name(selector_value)
elif selector_by == "x" or selector_by == 'xpath':
element = self.base_driver.find_element_by_xpath(selector_value)
elif selector_by == "s" or selector_by == 'css_selector':
element = self.base_driver.find_element_by_css_selector(selector_value)
else:
raise NameError("Please enter a valid type of targeting elements.")
return element
复制代码
面向对象编程思想的运用
封装后的方法如何被调用
使用上面的封装类,就须要指定特定的 selector
类型 | 示例(分隔符以逗号, 为例) |
描述 |
---|---|---|
id | "account" 或者 "i,account" 或者 "id,account" | 分隔符左右两侧不能够空格 |
xpath | "x,//*[@id="s-menu-dashboard"]/button/i" | |
css selector | "s,#s-menu-dashboard > button > i" | |
link text | "l,退出" | |
partial link text | "p,退" | |
name | "n,name1" | |
tag name | "t,input" | |
class name | "c,dock-bottom | |
具体调用示例
def login(self, account, password, keep):
""" 登陆系统 :param account: :param password: :param keep: :return: 返回保持登陆复选框的 checked 值 """
self.base_driver.type(self.LOGIN_ACCOUNT_SELECTOR, account)
self.base_driver.type(self.LOGIN_PASSWORD_SELECTOR, password)
current_checked = self.get_current_keep_value()
if keep:
if current_checked is None:
self.base_driver.click(self.LOGIN_KEEP_SELECTOR)
else:
if current_checked == "true":
self.base_driver.click(self.LOGIN_KEEP_SELECTOR)
actual_checked = self.get_current_keep_value()
self.base_driver.click(self.LOGIN_SUBMIT_SELECTOR)
sleep(2)
return actual_checked
复制代码
Page-Object设计模式的本质
Page Object设计模式是Selenium自动化测试项目的最佳设计模式之一,强调测试、逻辑、数据和驱动相互分离。
Page Object模式是Selenium中的一种测试设计模式,主要是将每个页面设计为一个Class,其中包含页面中须要测试的元素(按钮,输入框,标题等),这样在Selenium测试页面中能够经过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,须要改测试页面代码的状况。当页面元素id变化时,只须要更改测试页Class中页面的属性便可。
它的好处以下:
具体的作法以下:
Page 如何划分
通常经过继承的方式,进行按照实际Web页面进行划分
Page-Object 类如何实现
实现的示例
Page 基类
设计了一个基本的 Page类,以便全部的页面进行继承,该类标明了一个sub page类的基本功能和公共的功能。
全局变量: self.base_driver,让全部的子类都使用的。
# 基类的变量,全部继承的类,均可以使用
base_driver = None
复制代码
构造方法:
传递 driver的构造方法
# 方法
def __init__(self, driver: BoxDriver):
""" 构造方法 :param driver: ":BoxDriver" 规定了 driver 参数类型 """
self.base_driver = driver
复制代码
LOGIN_ACCOUNT_SELECTOR = "s, #account"
LOGIN_PASSWORD_SELECTOR = "s, #password"
LOGIN_KEEP_SELECTOR = "s, #keepLoginon"
LOGIN_SUBMIT_SELECTOR = "s, #submit"
LOGIN_LANGUAGE_BUTTON_SELECTOR = "s, #langs > button"
LOGIN_LANGUAGE_MENU_SELECTOR = "s, #langs > ul > li:nth-child(%d) > a"
LOGIN_FAIL_MESSAGE_SELECTOR = "s, body > div.bootbox.modal.fade.bootbox-alert.in > div > div > div.modal-body"
复制代码
成员方法:
每一个子类都须要的系统功能:
open
def open(self, url):
""" 打开页面 :param url: :return: """
self.base_driver.navigate(url)
self.base_driver.maximize_window()
sleep(2)
复制代码
全部子类(页面)都具备的业务功能
* select_app
* logout
复制代码
Sub Pages(s)子类
具体的页面的类,定义了某个具体的页面的功能
必须继承基类
class MainPage(BasePage):
复制代码
特定页面的业务
使用基类的 self.base_driver
成员变量
Tests 类
这部分描述的是具体的测试用例。
声明全局变量
base_driver = None
base_url = None
main_page = None
复制代码
调用各类页面(pages)
实例化Page
self.main_page = MainPage(self.base_driver)
复制代码
使用page的对象,调用成员方法
self.main_page.open(self.base_url)
self.main_page.change_language(lang)
复制代码
什么是数据驱动
主要的数据驱动方式有两种:
经过 CSV 文件 或者 MySQL 数据库,是主流的数据驱动方式。固然数据驱动也能够结合单元测试框架的参数化测试进行编写(此部分本文不作具体描述)。
不管使用了 哪种(CSV 或者 MySQL),读取数据后都要进行遍历操做。
使用 csv
import csv
csv_file = open("xxx.csv", "r", encoding="utf8")
csv_data = csv.reader(csv_file)
for row in csv_data:
# 进行测试
# 使用字典类型
data_to_test = {
"key1": row[0],
"key2": row[1]
}
csv_file.close()
复制代码
使用 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()
复制代码
须要掌握的知识点:
dict
类型如何生成测试报告
测试报告的种类
HTML 测试报告的生成
HTML测试报告须要引入HTMLTestRunner
HTMLTestRunner是基于Python2.7的,咱们的课程讲义基于Python3.x,那么须要对这个文件作必定的修改。
测试的示例代码以下
# 声明一个测试套件
suite = unittest.TestSuite()
# 添加测试用例到测试套件
suite.addTest(RanzhiTests("test_ranzhi_login"))
# 建立一个新的测试结果文件
buf = open("./result.html", "wb")
# 声明测试运行的对象
runner = HTMLTestRunner.HTMLTestRunner(stream=buf,
title="Ranzhi Test Result",
description="Test Case Run Result")
# 运行测试,而且将结果生成为HTML
runner.run(suite)
# 关闭文件输出
buf.close()
复制代码
在这里向你们推荐一个学习交流群,里面有小伙伴整理好的自动化资料上传在群文件,欢迎你们加群下载:1007119548,
若是以为这篇文章不错也能够转发给须要的朋友的哦!