软件测试入门④——测试脚本【讲义】

六天入门四_新版公众号首图_2018.10.23(2).png

Day 4 测试脚本讲义

经过了前面三天的课程内容,我们应该初步基本的掌握了手工测试。接下来我们来面对另一个高度的挑战,就是自动化测试。自动化测试是近期的一个热门概念,在一线城市的软件测试招聘中,经常会出现自动化测试的要求,而且在面试的过程中,企业和面试官也是会涉及到自动化测试相关的内容。接下来我们用两张图来说明自动化测试的内容。

首先我们来看看手工测试。

img
img

再来看自动化测试。

img
img

本次讲义,主要解决的问题如下:

  • 自动化测试的本质是什么?
  • Web UI 自动化测试的对象怎么理解?
  • Selenium 是一个什么样的工具?

注意:本讲义探讨的自动化测试是 Web UI 自动化测试。

0 主要内容

  • 1 T5_自动化测试分析
  • 2 T6_自动化测试环境准备
  • 3 T7_自动化测试脚本编写

1 T5_自动化测试分析

1.1 自动化测试的出发点

还记得上一届内容中对“回归测试”的描述么?在敏捷开发的实践中,每一次新版本的发布,都会要求对过往版本已经测试过的功能进行回归测试。一般会挑选重要的测试用例,加入到回归测试的用例集合中,然后在每一次的新版本中,重新执行。

这样的要求,如果单纯依靠手工测试,那么工作量会相当大,而且也会增加测试人员的疲倦情绪,于是,自动化测试就闪亮登场了。目前自动化测试其实主要解决的就是回归测试的问题,而不能够做到完全替代手工测试。

1.2 自动化测试的对象分析

本次讲义的内容,我们重点分析的就是 Web UI(浏览器界面)的自动化测试,并且我们已知“自动化测试就是人类让电脑替自己做测试执行”的过程,那么我们首先要分析一下,浏览器界面的执行因素。

如果你还不明白讲义在这里指代的内容是什么,那么我们用浏览器自带的开发者工具查看浏览器渲染的 html 源码。

image.png

浏览器渲染的内容是由一个个tag组成的。我们平时通过手工测试点击浏览器,实际上就是选择了浏览器渲染出来的 html 标签,并且对其进行操作的。

1.3 自动化测试的工具要求

自动化测试就需要做到下面的事情,即可。

  • 让电脑明白去找哪个浏览器的哪个标签
  • 找到标签后做什么样具体的操作
  • 操作以后,检查操作是否成功

2 T6_自动化测试环境准备

2.1 选择并安装编程语言

从上面一节,我们知道,让电脑替我们做事情,并且要让电脑给出检查结果,这是需要通过编程语言来实现的。因为“电脑不懂人话”……

现在主流的可以选择的编程语言进行测试,主要集中在 Python 和 Java 上。部分工具也会选择 C#。我们这里选择 Python。

Python 语言的选择,便捷

  • 测试人员的编程能力普遍不是很强,而Python作为一种脚本语言,不仅功能强大,而且语法优美,支持多种自动化测试工具,而且学习上手比较容易。
  • Python的社区发展比较好,有着非常多的文档和支持库,另外Python也可以在Web开发、数据处理、科学计算等纵多领域有着非常好的应用前景。
  • 对于有一定编程基础的人员,使用Python作为自动化测试的语言可以非常顺畅的转换,几乎没有学习成本。同时Python是标准的面向对象的编程语言,对于C#、Java等面向对象的语言有着非常好的示例作用,通过Python的示例可以非常轻松的触类旁通,使用其他语言进行Selenium2.0的WebDriver的使用。
  • 读音:/'paɪθən/
  • Python的创始人为Guido Van Rossum。1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为ABC 语言的一种继承。之所以选中Python(大蟒蛇的意思)作为程序的名字,是因为他是一个叫Monty Python的喜剧团体的爱好者。
  • Python 语言除了在自动化测试领域有出色的表现外,在系统编程,网络编程,web 开发,GUI开发,科学计算,游戏开发等多个领域应用非常广泛,而且具有非常良好的社区支持。也就是说学习和掌握python 编程,其实是为你打开了一道更广阔的大门。

安装 Python 语言

Python的官方网站:http://www.python.org

Python 目前并行了两套版本,2.x 和 3.x。如果你之前没有 Python 的使用经验,建议使用 Python 3.x 版本。两套版本互相不兼容,并且 Python 从 3.5(含)开始,不再支持 Windows XP 系统,请注意。

  • 选择安装目录

    • 3.4或者3.4以下的版本,都是 C:\python34
    • 3.5以上的目录,默认装个人文件夹,建议用类似上面的目录,比如C:\python35
    Snap1.jpg
  • 勾选添加环境变量

    勾选Add Python.exe to PATH

    Snap2.jpg
  • 安装过程中不要关闭弹出来的命令行窗口

  • 关于 Python 的安装,也可以选择一些第三方的Python 安装包,典型的有 Anaconda3,这样的包有丰富的第三方库,在使用 Python 的过程中会更加方便。

    Anaconda 的官网:https://www.continuum.io/anaconda-overview

2.2 设置测试工具与驱动

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 诞生于 2004 年,当在 ThoughtWorks 工作的 Jason Huggins 在测试一个内部应用时。作为一个聪明的家伙,他意识到相对于每次改动都需要手工进行测试,他的时间应该用得更有价值。他开发了一个可以驱动页面进行交互的 Javascript 库,能让多浏览器自动返回测试结果。那个库最终变成了 Selenium 的核心,它是 Selenium RC(远程控制)和 Selenium IDE 所有功能的基础。Selenium RC 是开拓性的,因为没有其他产品能让你使用自己喜欢的语言来控制浏览器。这就是 Selenium 1。

    在 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 工具包

      由于 安装好的 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
          

        • 卸载安装当前的 Selenium

          pip uninstall selenium
          
      • 当然,如果您的机器处于非接入互联网的环境,您可以事先下载 Selenium 的 Python 安装包,再进行手动安装。

  • 配置 浏览器 和 驱动

    • Selenium 2 可以默认支持Firefox 46.0或者更低版本,对于其他浏览器需要额外安装驱动。

    • Selenium 3 对于所有的浏览器都需要安装驱动,本文以 Chrome 和 Firefox、IE为例设置浏览器和驱动。

    • 浏览器驱动的配置

      • 首先,将下载好的对应版本的浏览器安装。
      • 其次,在 Python 的根目录中,放入浏览器驱动。
      • 最好再重启电脑,一般情况下不重启也可以的。

2.3 使用 IDE 进行脚本编写

  • JetBrains PyCharm 的介绍

PyCharm 是 JetBrains 公司针对Python推出的IDE(Integrated Development Environment,集成开发环境)。是目前最好的Python IDE之一。目前包含了两个版本:

  • 社区版,Community Edition
  • 专业版,Professional Edition
    • 付费
    • 比社区版主要多了Web开发框架

我们推荐使用免费的社区版本,进行Python脚本的编写和自动化测试执行。

PyCharm可以在官网下载,http://www.jetbrains.com

PyCharm 安装后,如果也安装过 Python 环境,可以直接进行操作。否则请在 1.2.3 中安装好 Python,再使用 PyCharm。

  • 安装按照默认的步骤安装

    • 使用方式

      • Create New Project:

        创建新的项目,选择项目创建的位置,选择Python的解释器

        Snap3.jpg
        Snap4.jpg
        • 设置location,项目的路径和名称
          • 名称必须以英文字母开头
          • 名称不可以有空格
          • 位置不可以在 C:\Pytho34中,应该放到普通的目录中
        • 设置interpreter
          • 一个电脑可以装多个 Python
          • 这里选择一个你需要的 Pythpn

        新建Python文件

        Snap11.jpg
        Snap8.jpg

        在创建的文件中编写第一个Python语句

        print("hello Python!")
        
        Snap9.jpg

        右键该文件,选择Run hello,运行该语句,在下面的运行框中会显示运行结果

        C:\Python35\python.exe D:/Git/WeekendSelenium/untitled/hello.py
        hello python!
        
        Process finished with exit code 0
        

        如图

        Snap10.jpg
      • Open

        打开已经存在的项目,比如别人发给你的项目,或者已经创建过的项目

        Snap5.jpg
        Snap7.jpg
  • 安装后进行设置如下:

    • 设置行号的显示

      永久设置。File --> Settings -->Editor -->Appearance , 之后勾选Show Line Numbers

      Snap12.jpg

    • 设置字体

      选择 Settings | Editor | Colors & Fonts | Fonts

      Save AS 主题

      选择 Source Code Pro(建议选择,等宽字体)

编写第一个脚本

通过上一节的环境安装成功以后,我们可以进行第一个对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)

      那么关于这个类的使用,只需要两个步骤:

      1. 实例化该类:d = Driver()
      2. 调用类的方法:d.head("中山路")
    • 了解上述例子和使用以后,我们来看具体的 Selenium 的使用。

  • 具体的对象的使用

    在面向对象的理念看来,任何的编码,都是由对象而来的,这里也不例外。和之前介绍 WebDriver 时候的描述对应,我们需要用到两种主要的类,并将其实例化。

    • WebDriver 类:主要靠直接实例化该类为对象,然后用其对象直接调用该类的方法和属性
    • WebElement 类:主要通过 WebDriver 类实例化的对象,通过对页面元素的查找,得到 WebElement 类的对象,然后调用该类的方法和属性。

    上述代码中,使用了一个 WebDriver 类 的对象,即第2行,声明了该类的对象,并赋值给变量 driver,接着变量 driver 作为 WebDriver 类的对象,使用了多个 WebDriver 类的方法。

    注意:Chrome 是 WebDriver 的子类,是 WebDriver 类的一种

    • get(url): 第5行,打开网址
    • find_element_by_css_selector(selector): 第12、17、22行都使用了该方法,同时通过对该方法的调用,分别各产生了一个 WebElement类的对象,we_accountwe_password和最后一个匿名的对象,并通过产生的三个对象,调用 WebElement 类的方法
      • clear():清理页面元素中的文字
      • send_keys(text):给页面元素中,输入新的文字
      • click():鼠标左键点击页面元素

    正是通过这样的面向对象的方式,产生 Web司机(WebDriver类的对象),并且通过 Web司机不懈的努力,寻找到各种 Web元素(WebElement类的对象)进行操作,这样便实现了 Selenium WebDriver 作为一款出色的浏览器测试工具,进行浏览器UI界面的自动化测试的代码编写和用例执行。

3 T7_自动化测试脚本编写

3.1 WebDriver 类的使用

通过上述最简脚本的使用,我们可以来进一步了解 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

  • API 使用: 用现成的类(大部分情况)的方法进行编程
    • WebDriver
    • WebElement
  • API 文档
    • 编程使用说明
    • 介绍了每个方法的使用
      • 方法的作用
      • 方法的参数
      • 方法的返回值

3.1.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()

3.1.2 元素定位操作

WebDriver提供了一系列的定位符以便使用元素定位方法。常见的定位符有以下几种:

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

那么我们以下的操作将会基于上述的定位符进行定位操作。

对于元素的定位,WebDriver API可以通过定位简单的元素和一组元素来操作。在这里,我们需要告诉Selenium如何去找元素,以至于他可以充分的模拟用户行为,或者通过查看元素的属性和状态,以便我们执行一系列的检查。

在Selenium2中,WebDriver提供了多种多样的find_element_by方法在一个网页里面查找元素。这些方法通过提供过滤标准来定位元素。当然WebDriver也提供了同样多种多样的find_elements_by的方式去定位多个元素。

尽管上述的方式,可以进行元素定位,实际上我们也是更多的用组合的方式进行元素定位。

依据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 &amp; 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的内容,请查看http://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 &amp; 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%按照手工的步骤进行操作。

比如步骤如下:

  1. 点击一个 <a id="customer_chosen">
  2. 自动产生了一个 <ul id="customer_list">
  3. 点击<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)')

3.1.3 鼠标事件操作

Web测试中,有关鼠标的操作,不只是单击,有时候还要做右击、双击、拖动等操作。这些操作包含在ActionChains类中。

常用的鼠标方法:

  • context_click() # 右击
  • double_click() # 双击
  • drag_and_drop() # 拖拽
  • move_to_element() # 鼠标停在一个元素上
  • click_and_hold() # 按下鼠标左键在一个元素上

例子:

# 方法模拟鼠标右键,参考代码如下:
# 引入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()

3.1.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')

3.1.5 截图操作

截图的方法:save_screenshot(file)

3.2 单元测试框架的引入

在上一节,我们对 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模组
  • 继承unittest.TestCase基类
  • 测试方法以test开头

unittest 并未使用 Java 语言常见的注解方式,依旧停留在 比较早期的 Java 版本中依靠方法名称进行识别的方式。主要有以下两个固定名字的方法:

  • setUp():在每个测试方法运行前,执行。是测试前置条件。
  • tearDown():在每个测试方法运行后执行,是测试清理操作。

具体的代码如下:

## 引入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)

需要注意步骤:

  1. 引入 unittest 模组
  2. 继承 unittest.TestCase 类
  3. 做测试用例的方法,方法以 test_ 开头
    1. 附加 setUp(), tearDown(), 在每个 test_ 方法执行前后 进行执行
    1. 附加 setUpClass(), tearDownClass()
      需要在 类实例化的对象,运行的开头和结尾进行执行。
      加了星号(*)的步骤,可以不用。

上述代码运行结果如下:

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 的断言,属于 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可选,用来解释失败的原因

3.3 测试用例类的编写

这里,我们需要结合 unittest 与 WebDriver 来一起编写 测试用例的类。

  • WebDriver 的实例化放到 setUp() 里面实现
  • WebDriver 实例化的对象的退出,放到 tearDown() 里面实现
  • 测试的步骤与断言,放到 test_ 开头的方法里

参考一个例子。

from time import sleep
from selenium import webdriver
import unittest

class RanzhiTest(unittest.TestCase):
    """
    用例 [rzat01]  系统登录模块能接受不同的语言和不同类型的用户在有效和无效范围内登录。
    """

    base_url = None
    main_page = None

    def set_up(self):
        """
        测试前置条件
        :param **kwargs:
        :return:
        """
        self.base_driver = webdriver.Chrome()
        self.base_url = "http://localhost/ranzhi/www/"

    def tear_down(self):
        """
        测试清理条件
        :return:
        """
        self.base_driver.quit()

    def test_01(self):
        """
        测试有效和无效用户登录
        :return:
        """
        account = "admin"
        password = "123456"
        
        self.base_driver.get(self.base_url)
        sleep(2)
        
        we_account = self.base_driver.find_element_by_id("account")
        we_account.clear()
        we_account.send_keys(account)
        
        we_password = self.base_driver.find_element_by_id("password")
        we_account.clear()
        we_account.send_keys(password)
        
        self.base_driver.find_element_by_id("submit").click()
        sleep(2)
        
        expected_url = self.base_url + "sys/index.html"
        actual_url = self.base_driver.get_url()
        self.assertEqual(expected_url, actual_url, "有效登录用户[%s]登录失败或者登录后跳转错误!" % account)
六天入门软件测试系列课程总纲
  • 相关学习

立师兄Linty:六天入门软件测试①——测试执行讲义

立师兄Linty:六天入门软件测试①——测试执行笔记

立师兄Linty:六天入门软件测试②——测试分析讲义

立师兄Linty:六天入门软件测试②——测试分析笔记

立师兄Linty:六天入门软件测试③——测试设计讲义

立师兄Linty:六天入门软件测试③——测试设计笔记

立师兄Linty:六天入门软件测试④——测试脚本讲义

立师兄Linty:六天入门软件测试④——测试脚本笔记

立师兄Linty:六天入门软件测试⑤——测试编程讲义

立师兄Linty:六天入门软件测试⑤——测试编程笔记

立师兄Linty:六天入门软件测试⑥——测试报告讲义

立师兄Linty:六天入门软件测试⑥——测试报告笔记

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容