Python测试(二)

数据驱动

  • 安装:pip install ddt
  • Demo,数据打包,给测试类、测试case装饰上,这就是PO模型+UT+DDT
    import ddt
    import unittest
    
    @ddt.ddt
    class DataTest(unittest.TestCase):
        @ddt.data(
            ['1', '2'],
            [3, 4],
            [5, 6]
        )
        @ddt.unpack
        def test_data_add(self, a, b):
            print(a+b)
    
    if __name__ == '__main__':
        unittest.main()
    
  • PO模型+UT+DDT测试流程
    • 将主要的逻辑放在business层,case层逻辑还是最简化,这里主要体现出数据驱动case
    • 以测试邮箱输入失败的case为例;输入错误格式的邮箱地址 -> 页面显示错误信息 -> assertFalse执行成功,完全体现数据驱动
      import os.path
      import time
      
      import ddt
      import unittest
      from selenium import webdriver
      
      class FirstDdtCase(unittest.TestCase):
          def setUp(self) -> None:
              self.driver = webdriver.Chrome()
              self.driver.get("'http://www.5itest.cn/register'")
              self.login = RegisterBUsiness(self.driver)
      
          def tearDown(self) -> None:
              time.sleep(2)
              for method, error in self._outcomes.errors:
                  if error:
                      case_name = self._testMethodName + '.png'
                      file_path = os.path.join(os.getcwd(), '/report/', case_name)
                      self.driver.save_screenshot(file_path)
      
              self.driver.close()
      
          @ddt.data(
              # 邮箱,用户名,密码,验证码,错误信息标识,错误信息内容
              # ['email', 'username', 'password', 'code', 'assertCode', 'assertText'],
              ['666', 'roy', 123, 'xxxx', 'user_email_error', '请输入有效的电子邮件地址']
          )
      
          @ddt.unpack
          def test_register_case(self, email, username, password, code, assertCode, assertText):
          	# 这里封装的接口只需要传入数据,剩下的都在business做,完全体现data驱动
              # register_function(business层)包含注册的全流程,并不针对email_error
              # 注册时不同的场景也都是通过data体现的,email传不合法值,assertCode/assertText给上对应的值,就OK,完全体现ddt
              email_error = self.login.register_function(email, username, password, code, assertCode, assertText)
              return self.assertFalse(email_error, '测试失败')
      
    • 幕后的page层和config文件要对应上
  • 文件形式实现数据驱动
    • 在config下创建data.xls文件,存放数据,每个单元格数据都要搞成文本格式
    • 安装操作Excel的包:pip install xlrd
      import xlrd
      
      class ExcelHandle:
          def __init__(self, ex_path=None, index=None):
              if ex_path is None:
                  ex_path = '../conf/data.xls'
              if index is None:
                  # sheet
                  index = 0
              self.data = xlrd.open_workbook(ex_path)
              self.table = self.data.sheets()[index]
              self.rows = self.table.nrows # 这张sheet的行数
      
          def get_data(self):
              result = []
              for row in range(self.rows):
                  r = self.table.row_values(row)  # 列表
                  result.append(r)
              return result
      
      from util.excel_handle import ExcelHandle
      
      DATA = ExcelHandle.get_data()
      
      class FirstDdtCase(unittest.TestCase):
      	...
      	@ddt.data(*DATA)
          def test_register_case(self, data):
          	# 再次解包
              email, username, password, code, assertCode, assertText = data
              email_error = self.login.register_function(email, username, password, code, assertCode, assertText)
              return self.assertFalse(email_error, '测试失败')
      

关键字模型

  • DDT通过数据文件减少了我们的case量,那如何让没代码基础的人也能写测试代码?
  • 以测试注册为例,我们发现主要包含两个操作:输入,点击
  • 还是结合DDT的思想,所以我们搞了个Excel表格,什么操作,调用哪个方法都写清楚,直接用数据驱动
  • 这里的“操作”就是所谓的关键字,case步骤都是通过这一列判别的
    1
  • 这个关键字驱动也是按照流程一步步执行
  • 这里废话太多不想说了,直接看RobotFramework,结合Python源码看一看
    • 层级关系:Keywords -> TestCases -> Suites
    • 官方说这是关键字驱动的框架,关键字描述的功能用python封装,可能是因为Case都是调Keyword组装的;但更像BDD(给不会写代码的人用的)
    • 这个不是DDT的思想,但也比较流行,记录清楚流程后会在这里更新笔记

行为驱动

  • BDD(behavior driven development)像说话一样写Case
  • 框架:Behave
  • 安装 pip install behave pip install pyhamcrest
  • 来个简单的例子看看怎么用
    • 定义目录结构
      1
    • register.feature文件
      Feature: Register User
        As a developer
        This is my first bdd project
        Scenario: open register website
          When I open the register website
          Then I expect that the title is "注册"
      
    • environment.py,设置环境变量,注意:文件名都是behave规定的,不要乱写
      from selenium import webdriver
      
      def before_all(context):
          '''
          :param context: 全局超级变量
          :return:
          '''
          context.driver = webdriver.Chrome()
      
      def after_all(context):
          context.driver.close()
      
    • register_user.py 实现具体逻辑
      from behave import *
      
      use_step_matcher('re')
      
      @when('I open the register website')
      def step_register(context):
          context.driver.get('http://www.5itest.cn/register')
      
      # 匹配除"的所有字符 [^"]*,这个要和feature文件中keyword的定义一致;可以直接用 .*
      @then('I expect that the title is "([^"]*)"')
      def step_register_exp(context, title_name):
          title = context.driver.title
          assert title_name in title
      
    • terminal执行
      2
    • 可以发现,和robotframework的流程基本一致,Feature相当于Suite,Scenario相当于TestCase,When xxx相当于Keyword,底层逻辑在register_user用Python实现
  • 接下来在新的case(scenario)填写具体的注册信息,注意:这里的and和Python关键字冲突,需要大写
    4
    3
    5
  • 结合PO模型,封装我们的register_user
    • 新建lib文件夹
      6
    • 基础类,主要定义基础操作,通用
      class BasePage:
          def __init__(self, driver):
              self.driver = driver
      
          # 打开网页
          def get_url(self, url):
              self.driver.get(url)
      
          def get_title(self):
              return self.driver.title
      
          # 定位元素
          def find_element(self, *loc):
              self.driver.find_element(*loc)
      
    • register_page.py,针对某个feature
      from features.lib.pages.base_page import BasePage
      from selenium.webdriver.common.by import By
      
      class RegisterPage(BasePage):
          def __init__(self, context):
              # 使用父类的初始化方法
              super(RegisterPage, self).__init__(context.driver)
      
          def send_useremail(self, useremail):
              self.find_element(By.ID, 'register_email').send_keys(useremail)
      
          def send_username(self, username):
              self.find_element(By.ID, 'register_nickname').send_keys(username)
      
    • 使用我们封装的方法
      from behave import *
      from features.lib.pages.register_page import RegisterPage
      
      use_step_matcher('re')
      
      @when('I open the register website')
      def step_register(context):
          # context.driver.get('http://www.5itest.cn/register')
          RegisterPage.get_url('http://www.5itest.cn/register')
      
      @then('I expect that the title is "([^"]*)"')
      def step_register_exp(context, title_name):
          title = RegisterPage.get_title()
          # title = context.driver.title
          assert title_name in title
      
      @when('I set with useremail "([^"]*)"')
      def step_register(context, useremail):
          RegisterPage(context).send_useremail(useremail)
      
      @when('I set with username "([^"]*)"')
      def step_register(context, username):
          RegisterPage(context).send_username(username)
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑞士_R

修行不易...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值