美文网首页预见·软件测试技术
六天入门软件测试⑤——测试编程讲义

六天入门软件测试⑤——测试编程讲义

作者: 厲铆兄 | 来源:发表于2018-11-08 23:37 被阅读18次
    Day 5 测试编程讲义

    Day 5 测试编程讲义

    本篇讲义非常偏技术属性,需要用到比较多编程知识。阅读此讲义,需要对以下技术有一定了解:

    • 数据库表存储
    • 循环与分支语句
    • 基础设计模式
    • 面向对象封装
    • Python 库的使用

    0 主要内容

    • 1 T8_测试数据驱动
    • 2 T9_测试业务抽离
    • 3 T10_底层驱动封装

    1 T8_测试数据驱动

    1.1 什么是数据驱动

    • 什么是数据驱动

      主要的数据驱动方式有两种:

      • 通过 文本文件或者 Excel 文件存储数据,并通过程序读取数据,遍历所有的行
      • 通过数据库存储数据,并通过程序和 SQL 脚本读取数据,遍历所有的行

      通过 CSV 文件 或者 MySQL 数据库,是主流的数据驱动方式。当然数据驱动也可以结合单元测试框架的参数化测试进行编写(此部分本文不做具体描述)。

      无论使用了 哪一种(CSV 或者 MySQL),读取数据后都要进行遍历操作。

    1.2 使用 csv

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

    1.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()
    
    • 需要掌握的知识点:
      1. python的字典类型 dict 类型
      2. python的读写文件
      3. python的读写数据库
      4. for循环
      5. 注意资源的释放
        1. 关闭数据库游标和连接
        2. 关闭文件

    2 T9_测试业务抽离

    2.1 Page-Object设计模式本质

    • Page-Object设计模式的本质

      Page Object设计模式是Selenium自动化测试项目的最佳设计模式之一,强调测试、逻辑、数据和驱动相互分离。

      Page Object模式是Selenium中的一种测试设计模式,主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题等),这样在Selenium测试页面中可以通过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,需要改测试页面代码的情况。当页面元素id变化时,只需要更改测试页Class中页面的属性即可。

      它的好处如下:

      • 集中管理元素对象,便于应对元素的变化
      • 集中管理一个page内的公共方法,便于测试用例的编写
      • 后期维护方便,不需要重复的复制和修改代码

      具体的做法如下:

      1. 创建一个页面的类
      2. 在类的构造方法中,传递 WebDriver 参数。
      3. 在测试用例的类中,实例化页面的类,并且传递在测试用例中已经实例化的WebDriver对象。
      4. 在页面的类中,编写该页面的所有操作的方法
      5. 在测试用例的类中,调用这些方法

    2.2 Page 如何划分

    一般通过继承的方式,进行按照实际Web页面进行划分。

    • 主页
    • 子模块主页
      • 分类页
      • 详情页
        • 查看
        • 编辑
        • 增加

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

        1. 实例化Page

          self.main_page = MainPage(self.base_driver)
          
        2. 使用page的对象,调用成员方法

          self.main_page.open(self.base_url)
          self.main_page.change_language(lang)
          

    3 T10_底层驱动封装

    3.1 为什么需要封装 Selenium

    • 什么是封装

      封装是一个面向对象编程的概念,是面向对象编程的核心属性,通过将代码内部实现进行密封和包装,从而简化编程。对Selenium进行封装的好处主要有如下三个方面:

      • 使用成本低
        1. 不需要要求所有的测试工程师会熟练使用Selenium,而只需要会使用封装以后的代码
        2. 不需要对所有的测试工程师进行完整培训。也避免工作交接的成本。
        3. 测试人员使用统一的代码库
      • 维护成本低
        1. 通过封装,在代码发生大范围变化和迁移的时候,不需要维护所有代码,只需要变更封装的部分即可
        2. 维护代码不需要有大量的工程师,只需要有核心的工程师进行封装的维护即可
      • 代码安全性
        1. 对作为第三方的Selenium进行封装,是代码安全的基础。
        2. 对于任何的代码的安全隐患,必须由封装来解决,使得风险可控。
        3. 使用者并不知道封装内部的代码结构。

    3.2 封装的概念与基本操作

    • 关键方法的封装思路

      封装的具体示例:

      • 找到一个指定输入框(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()
        
      • 找到一个指定的frame,并且切换进去

        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,并且通过index进行选择

        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_...)
      • 支持各种的查找:8种方式都需要支持,必须通过 selector 显示出分类
        • selector中需要包含一个特殊符号
        • 实例化 封装好的类的时候,需要约定好是什么特殊符号
          1. 强制性用硬编码 hard code来实例化,例如 , 或者 ? 或者 其他非常用字符 =>
          2. 或者,构造方法中,传递 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
      

    3.3 测试报告的生成

    • 如何生成测试报告

    • 测试报告的种类

    • 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()
      
    六天入门软件测试系列课程总纲
    • 相关学习

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

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

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

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

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

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

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

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

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

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

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

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

    相关文章

      网友评论

        本文标题:六天入门软件测试⑤——测试编程讲义

        本文链接:https://www.haomeiwen.com/subject/cnogxqtx.html