自动化测试(6) | Web UI 自动化测试方案

Web项目的 UI 自动化测试方案

有用的链接:

  1. 自动化Selenium的Python文档
    http://www.jianshu.com/p/4ce5ecef5f6c
  2. 自动化Selenium的Java文档
    http://www.jianshu.com/p/00616647ddae
  3. 自动化Selenium的源代码
    https://coding.net/u/lintyleo/p/ProSelenium/git

项目讨论

  • 项目中符合自动化测试的部分有哪些?(目标和范围 scope, 准入准出标准)
    • 稳定的需求点、变动较少的页面
    • 每日构建后的测试验证 daily build
    • 比较频繁的回归测试
    • 需要在多平台上运行的相同测试案例、组合遍历型的测试、大量的重复任务
  • 自动化用例在整个项目的测试用例的覆盖率
    • 一般的要求 50% +
    • 重点的要求 80% +
    • 根据项目的具体要求,变动特别大的项目需要额外单独考虑覆盖率

团队建设

建立自动化测试的组,理想状态下有4个人员,测试开发、中高级自动化测试工程师、2个初级自动化工程师;非理想的情况下,可能只有一个人。

  • 测试开发:

    基础答案:自动化框架的建设,确定自动化框架的设计模式、第三方代码工具的封装、中间公共模块的设计和调用、测试用例、测试套件的管理和执行、测试报告和测试结果的输出(文件输出和邮件通知)

    可选高级:如果可能的话,需要搭建持续集成服务器(CI,Continuous Integration Server)的环境,进行持续交付和自动化的冒烟测试等。

    培训的任务,需要将设计的框架以及封装的驱动,对其他成员进行培训。

    有自动化方案的实施经验、有开发背景、以及持续集成的背景等。

  • 中高级自动化测试工程师:

    配合测试开发人员,实施测试框架的建设。主要负责中间公共模块的实现和实例化等,以及部分高难度和流程复杂的自动化用例脚本编写和调试等工作。

    有参与过自动化方案的建设、脚本编写经验丰富、会代码调试、懂Web测试等。

  • 初级自动化测试工程师:
    根据中间公共模块的设计,进行实例化公共模块、方法组合,实现自动化用例脚本的编写。
    有计算机编程思维、有代码经验、可以读懂脚本和HTML等。

  • 若只有我一个人:
    首先实现自动化用例的维护和执行。在这个基础上不断的抽取实现公共模块的设计以及测试报告的生成等工作。通过经验的积累,以及后续人员的补充,早日做好自动化框架的建设工作。

技术方案

Selenium WebDriver、Python(unittest) Java(JUnit)、CI Server

技术方案:
选择Python + Selenium 的技术方案。

首先技术工具是免费的,Python的工具用PyCharm社区版,Selenium的WebDriver是开源工具。利用比较简洁的Python语言进行自动化测试,对于人员的学习成本来讲比较实用,学习时间短,有优势。

另外Python自带的unittest单元测试框架可以很方便的实现自动化用例的设计和执行以及自动化用例套件的管理等任务。Python是纯面向对象的语言,后续也可以过渡到Java + Selenium进行更加丰富的自动化测试。
此外,可以选择Jenkins作为持续集成服务器,配合Python+Selenium的方案进行自动化冒烟测试。

源代码管理工具(VCS, version control system)

选择SVN(git)作为代码的源代码管理工具。集成在PyCharm中的步骤如下

  1. 把代码放到SVN在本地签出(check out)的文件夹目录中,例如 D:\SVN\XXProject\Trunck
  2. 用PyCharm打开 刚刚部署的代码
  3. 选择PyCharm的 VCS|Enable VCS integration,选择 Subversion(svn) 或者 Git
  4. 右键项目文件的根目录,选择 Subversion | add to VCS
  5. 右键项目文件的根目录,或者选 VCS | Commit Directory...
  6. 每天打开代码后,首先 Subversion | update project

硬件:
硬件的要求不高,主要需要独立的测试环境。另外测试人员用的电脑最好是Windows桌面操作系统,需要安装Firefox浏览器,避免47.0的最新版本。测试人员最好也使用Chrome浏览器辅助进行Web元素的定位。

Selenium 学习总结

Selenium IDE (火狐的插件)

  1. Selenium IDE
    一个基于火狐浏览器的插件
    1. 录制(需要确保右上角的录制按钮是按下去的),为了我们熟悉Selenium WebDriver/ RC
    2. 保存和另存为测试用例
    3. 无论是否保存用例,都可以直接回放
      • 导出测试用例的对应的编程脚本
      1. C# Nunit WebDriver
      2. Java Junit / testNG WebDriver
      3. Python unittest WebDriver
      4. Ruby Test::Unit WebDriver
    4. 插入、编辑、删除命令
    5. 学习Selenium的WebDriver/ RC

Python + Selenium WebDriver

  1. 搭建环境步骤
    a 安装 python3.x (windows xp 不支持 python3.5+)
    b 设置环境变量 path(安装时候也可以解决 勾上 add python.ext to path)
    c 安装Selenium 用pip命令安装 pip install -U selenium
    有无问题?
    1. 路径,pip可能定位不到,pip也可能定位到别的文件夹(perl)
      方案:cd c:\python34\scripts
    2. 外网ip问题。交换机的原因,局域网里面是同一个ip,造成安装超时
    3. 火狐的版本 46.0以及以下
    4. windows 7 x64以上的系统,需要用管理员模式运行cmd,否则会安装失败。
  2. 普通的使用
    定位方式
    by id, name, class_name, tag_name, css_selector, xpath, link_text, partial_link_text
    主要用的 是 id,name, css_selector, xpath, link_text

Selenium 基本使用

Python + Selenium 环境搭建

  1. 安装Python,3.x(3.4 3.5 3.6)
    注意:环境变量path
    需要添加 "C:\Python34;C:\Python34\Scripts;"到Path
    步骤:

    1. 右键 我的电脑 属性 | 高级系统设置 | 环境变量
    2. 系统变量 | Path 并 双击 | Ctrl+C | 编辑 | Ctrl+V
  2. 安装 Selenium
    cmd | pip install -U selenium

    -U = --upgrade 升级安装

    类似于linux命令
    command -x param
    容易出现的问题

    1. 环境变量
      cd C:\Python34\Scripts
      再安装
    2. 管理员权限
      右键 管理员方式运行

WebDriver的基本使用

  1. 实例化一个WebDriver的对象(使用默认的火狐浏览器 v46或者以下)
    driver = webdriver.Firefox()

    注意,一定要写括号,代表实例化对象

  2. 打开网址
    driver.get("http://localhost/ranzhi/www")

  3. 查找元素
    用id : driver.find_element_by_id("account")
    用name: driver.find_element_by_name("password")
    用xpath css_selector
    对元素的操作
    clear()
    click()
    send_keys()

  4. 使用python的休眠,给浏览器留出时间加载页面
    sleep(2)

  5. 关闭退出火狐
    driver.quit()

selenium WebDriver的进阶操作

  1. unittest 单元测试框架的使用

    验证:断言 self.assertEqual(expected, actual, msg)

    管理测试用例: 每一个以 test_ 开头的方法

    可以在自动化测试用例中忽略的部分:
     编号 名称 优先级 模块(项目) ## 忽略
    必须在自动化用例中强调的部分:
        前置条件    setUp()
        输入数据    
        步骤       test_ 开头的方法
        预期结果   test_ 开头的方法
        清理      tearDown()
    

    新增的一个知识点: 测试的初始化 以及 测试的结束

    setUpClass() 和 tearDownClass()

    注意 上述方法 与 setUp() 和 tearDown() 的调用顺序

  2. 定位方式的使用

    • css_selector

      方法调用:

      driver.find_element_by_css_selector("#langs > button")
      

      CSS的3种选择器:

      1. id: 使用#+id
      2. tag: 使用tag
      3. class: 使用.+class

      CSS selector的获取

      可以使用开发者工具,定位的元素,右键 | 复制 selector(复制CSS路径)

      • Chrome使用了相对路径 #langs > button
      • Firefox(firebug)使用了绝对路径 html.screen-desktop-wide.device-desktop body.m-user-login div.container div#login div.panel-head div.panel-actions div#langs.dropdown.open button.btn
    • class_name

      通过 class定位元素,必须保持 class唯一,否则定位会失败

      driver.find_element_by_class_name("btn")
      

  • link_text 和 partial_link_text

    • 文字选择正确就行了
  • xpath

    • chrome内核的开发者工具,复制的是相对的xpath
    • firefox内核的开发者工具(firebug),复制的绝对的xpath

  1. 特定的元素的处理

    • frame

      <iframe>
       <html>
           <head></head>
            <body>
                 <我们需要定位的元素>
            </body>
        </html>
      </iframe>
      

      上述代码中,我们需要定位的元素,处于 一个frame中:我们需要先切换到对应的frame中,然之操作,之后再退出

      ## 我们需要先切换到对应的frame中
      ## 切换frame
      driver.switch_to.frame("frame_name")
      ## 或者用保险的办法切换frame
      frame_element = driver.find_element_by_css_selector("#frame_id")
      driver.switch_to.frame(frame_element)
      ## 开始做定位操作
      ## <----------------->
      ## 结束做定位操作
      ## 切换frame必须成对的出现。
      ## 退出切换的frame到默认的网页html中
      driver.switch_to.default_content()
      

    • select

      select定位有个前提,元素标签必须是 select

      <select>
        
      </select>
      
      ## 部门是个 select 元素  <select></select>
      ## 需要3步骤:
      ##    1. 找到 select 的元素,赋值给变量 dept_select
      ##    2. 实例化 dept_select 为 Select()的对象 为 user_adding_dept
      ##    3. 调用 user_adding_dept 的方法 select_by_index()给元素赋值
      

      示例代码

      # 找到 select 的元素,赋值给变量 dept_select
      dept_select = driver.find_element_by_id("dept")
      
      # 实例化 dept_select 为 Select()的对象 为 user_adding_dept
      user_adding_dept = Select(dept_select)
      
      # 调用 user_adding_dept 的方法 select_by_index()给元素赋值
      user_adding_dept.select_by_index(2)
      
    • 清除 cookie

      在打开浏览器的时候,需要清理cookie。

      from selenium import webdriver
      
      driver = webdriver.Firefox()
      driver.delete_all_cookies()
      driver.get("...")
      

数据驱动的方式编写用例

主要有两种形式进行数据驱动

数据驱动:用外部数据来驱动测试用例的执行

  • 数据库驱动:MySQL、Oracle、PostgreSQL、SQL Server

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

  • 数据文件驱动:csv文件最典型、xml文件、txt文件

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

  • 需要掌握的知识点:

    1. python的字典类型 dict 类型
    2. python的读写文件
    3. python的读写数据库
    4. for循环
    5. 注意资源的释放
      1. 关闭数据库游标和连接
      2. 关闭文件

模块化的方式编写用例

需要将每个用例都需要用的公共的功能,抽取出来,放到一个公共类中,作为模块化

  1. 创建一个Python Package "common",在里面创建一个ranzhi_common.py文件,里面添加RanzhiCommon类

    • login() : 普通登录,默认用admin/123456登录
    • logout():退出系统
    • login_by_dict(user_to_login):使用字典类型的参数进行登录,user_to_login 是一个字典类型 dict
    • change_language(lang):zh_CN, en_US, zh_TW 切换语言,需要参数传递要切换的语言
    • select_app(app):crm, admin, oa, cash, team 选择App,需要传递选择 后台管理 | 客户管理 。。。
    • select_menu_for_admin(menu): organization 选择了后台管理以后,选择菜单 组织
    • add_user(user_to_add): 使用字典类型的参数进行添加
  2. 创建测试用例的类,比如 ranzhi_test_01.py ranzhi_test_02.py ranzhi_test_03.py... 在这些类中调用上面创建的类,一般会放到setUp() 中

    import unittest
    from common.ranzhi_common import RanzhiCommon
    
    class RanzhiTest01(unittest.TestCase):
        
        def setUp(self):
            ranzhi_common = RanzhiCommon()
            
        def test_xxx(self):
            ranzhi_common.login()
            ##....
    

Selenium 自动化测试方案

基础方案

  1. 使用unittest,主要是用 TestCase(测试用例)
  2. 使用模块化(基本的模块化,抽取公共模块)
  3. 使用数据驱动的方式(主要包括 数据库的形式 和文件读取 )
  4. 定制执行需要测试的用例,使用unittest,主要用的是TestSuite(测试套件)、和 TextTestRunner(文本测试运行器)[或者HTMLTestRunner]
  5. 生成测试报告(发送报告到指定邮箱)
自动化测试方案(基础版).jpg
自动化测试方案(高阶版).jpg

方案实现的具体步骤:

  1. 使用PyCharm创建新的PurePython项目

  2. 在项目中创建如下的文件夹(python package)

    • TestCase
    • Data
    • Common
    • TestRunner
    • Report
  3. 分别实现上述模块

    1. 在Data中存放数据,CSV文件,或者也可以放到数据中,使用数据驱动

      ## 使用csv文件
      csv.reader()
      ## 使用数据库(MySQL),可以参考www.imooc.com的python操作MySQL的视频
      cur.fetchall()
      

    2. 在Common中,创建测试公共模块,实现模块化的操作

      ## __init__(self, driver: webdriver.Firefox, base_url)
      self.common = RanzhiCommon(self.driver, self.base_url)
      

    3. 在TestCase创建自动化测试用例,注意使用unittest.TestCase

      def setUp()
      def tearDown()
      det test_xxx()
      

    4. 在TestRunner创建测试执行器和测试套件,使用unittest.TestSuiteHTMLTestRunner(第三方)

      test_suite = unittest.TestSuite()
      test_suite.addTest(RanzhiLogonTc01("test_xxx"))
      
      html_test_runner = HTMLTestRunner(stream=report_file, title="xxx", description="dddd")
      html_test_runner.run(test_suite)
      

    5. 在项目根目录创建main.py,作为测试项目的主入口,运行测试。

  4. 检查测试报告

Selenium 可能遇到的问题

有无问题?

a <iframe> 
    f1 = find_element_by_css_selector(frame)
    switch_to.frame(f1)  
b <select>
    s1 = find_element_by_css_selector(select)
    ss1 = Select(s1)
    ss1.select_by_index(0)
    ss1.select_by_value("人事部")
c 编码问题 utf-8
d 时间等待问题 sleep(5)

单元测试框架
unittest
解决了什么问题?

unittest.TestCase
    前置条件
    清理
    测试过程步骤
    断言 assertEqual(期望值, 实际结果, 错误提示)
    运行测试 test_开头的方法
unittest.TestSuite
    addTest(xxx("test_batch_login"))
    自定义的添加测试用例,并执行
unittest.TextTestRunner
    run(suite)
    执行创建并维护好的测试套件
    
test_runner  test_suite  test_case
测试运行器    测试套件     测试用例

模块化操作

面向对象的实践: 
1. 编写一个类,类里面描述公共的方法
2. 实例化这个类,调用这个类的方法
有无问题?
类的构造方法,需要传递 webdriver.Firefox()
    self.common = RanzhiCommon(self.browser, self.base_url)

数据驱动测试
读取csv,循环每一行数据进行操作
读取MySQL,用例存到MySQL中

1. 中文编码的问题。 
    # coding="utf-8"
    读csv的时候,添加 encoding='utf-8'
    csv.reader(open("xxx.csv", "r", -1, encoding="utf-8")
2. 循环放到最外层
3. 文件路径在控制台读取不到的问题,需要用绝对路径来指向文件

封装WebDriver

  • 避免第三方代码的威胁,防止大批量修改测试用例
  • 节约人力的成本(如果封装以后,不需要每个自动化测试工程师都会WebDriver)
  • 标准化自动化用例的操作,只需要调用公共的标准模块就好。

推荐阅读更多精彩内容