Unittest框架写接口测试用例

上一篇 / 下一篇  2020-10-12 09:54:09

I.TestCase作用:是最小的测试单元,用于检查特定输入集合的特定返回值,可以用来创建新的测试用例
  II.编写测试用例规则
  (1)创建一个测试类,必须继承unnittest模块的TestCase类
  (2)创建一个测试方法,必须以"test"开头
  (3)调用被测试类,传入初始化数据
  (4)调用被测试方法,得到计算结果。用assertEqual()断言是否与预期结果相同。
  (5)调用unnitest的main()执行测试用例
  .:一条运行通过的测试用例
  F:一条运行失败的测试用例
  E:一条运行错误的测试用例
  s:一条运行跳过的测试用例
  III.定义测试方法:以test 开头的方法就是一条测试用例
  (1)准备用例数据:用例的参数(datas)和预期结果(excepted)
  (2)执行功能函数,获取实际结果:result = res['code']
  (3)对比实际结果和预期结果:self.assertEqual(excepted, result)【用例执行通没通过的评判标准:断言异常】
  IV.测试用例包含内容
  (1)数据处理
  ·读取excel数据
  ·数据驱动:ddt方法
  ·替换excel数据:header加token,可变数据替换(正则替换)
  ·excel数据格式处理:比如str转json-->eval,编号str转为int
  (2)错误写入日志
  (3)执行结果和对比结果写入excel
  (4)查询数据库结果
  完整代码:
  import os
  import unittest
  from library.ddt import ddt,data # ddt数据驱动
  from com.doexcel import DoExcel # 操作excel数据
  from com.contants import DATA_DIR # 测试用例模块所在目录
  from com.myconf import conf #读取配置文件
  from com.log import my_log # 日志处理对象
  from com.handle_data import Header,replace_data,TestData # 数据处理
  from com.handle_request import HandleRequest # http请求方法
  from com.mysql import MySql # 导入数据库
  excel_path = os.path.join(DATA_DIR,"testexcel.xlsx") # excel路径
  @ddt
  class TestClassName(unittest.TestCase):
      excel = DoExcel(excel_path, "register")
      cases = excel.read_data()  # 读取excel数据
      http = HandleRequest() # 创建http请求对象
      mysql = MySql()  # 创建数据库对象
      @classmethod
      def setUpClass(cls):
          my_log.info("---------------开始执行TestClassName类测试用例---------")
      def setUp(self):
           pass
          # 每条用例执行之前都会执行
      @data(*cases)
      def test_methodName(self,case):
          # -----------------------第一步:准备用例数据-------------------------------------
          # 用例方法参数
          # 请求url
          url = conf.get('url_info', 'url_base')+ case["url"]
          # 请求方法
          method = case["method"]
          # 数据替换
          case['data'] = replace_data(case['data'])
          # excel中读的数据类型str转为json
          data = eval(case["data"])
          # 请求头
          headers = getattr(Header, 'headers')
          # 将token加到请求头中
          headers['Authorization'] = getattr(TestData, 'token')
          # 预期结果
          expected = eval(case['expected'])
          # 该用例在表单中所在行
          row = int(case['case_id']) + 1
          #-----------------------第二步:发送请求到接口,获取实际结果-------------------------------------
          result = self.http.send(url = url,method = method,json = data,headers = headers).json()
          TestResult = 'FAIL'  # 测试对比结果:默认为失败
          try:
              self.assertEqual(expected ['code'], result['code'])
              TestResult = 'PASS'
              my_log.info("执行用例:{0}--->执行通过".format(case["title"]))  # 将测试结果写入日志文件
          except AssertionError as e:
              my_log.info("执行用例:{0}--->未执行通过,出错位置为:{1}".format(case["title"], e))  # 将测试报错结果写入日志文件
              raise e
          finally:
              self.excel.write_data(row = row, column = 9,value = str(result)) # 回写执行结果
              self.excel.write_data(row = row,column = 9,value = TestResult) # 回写比对结果
      def tearDown(self):
          pass
          # 每条用例执行之后都会执行
      @classmethod
      def tearDownClass(cls):
          my_log.info("---------------结束执行TestClassName类测试用例---------")
  if __name__ == '__main__':
      unittest.main()
  说明:
  ·配置文件涉及内容:
  [url_info]
  url_base = url
  content-type = application/json; charset=UTF-8
  [test_info]
  ....
  ·ddt实现接口报告中按接口名显示用例(修改ddt中的mk_test_name())
  def mk_test_name(name, value, index=0):
      """
      Generate a new name for a test case.
   
      It will take the original test name and append an ordinal index and a
      string representation of the value, and convert the result into a valid
      python identifier by replacing extraneous characters with ``_``.
   
      We avoid doing str(value) if dealing with non-trivial values.
      The problem is possible different names with different runs, e.g.
      different order of dictionary keys (see PYTHONHASHSEED) or dealing
      with mock objects.
      Trivial scalar values are passed as is.
   
      A "trivial" value is a plain scalar, or a tuple or list consisting
      only of trivial values.
      """
      
      # Add zeros before index to keep order
      index = "{0:0{1}}".format(index + 1, index_len)
      # 添加了对字典数据的处理
      if not is_trivial(value) and type(value) is not dict: #如果不符合value的要求,则直接返回用例名称_下标作为最终测试用例名字
          return "{0}_{1}".format(name, index)
      #  如果数据是字典,则获取字典当中的api_name对应的值,加到测试用例名称中
      if type(value) is dict: 
          try:
              value = value["case_name"]   #case_name作为value值
          except:
              return "{0}_{1}".format(name, index)
      try:
          value = str(value)
      except UnicodeEncodeError:
          # fallback for python2
          value = value.encode('ascii', 'backslashreplace')
      test_name = "{0}_{1}_{2}".format(name, index, value)
      return re.sub(r'\W|^(?=\d)', '_', test_name)
  ·数据处理文件
  import re
  from com.myconf import conf
  import random
  # 请求头
  class Header:
      headers = {'X-Lemonban-Media-Type': conf.get('Content-Type': conf.get('url_info','Content-Type'),'Authorization': None}
  class TestData:
      """专门用来保存一些替换的数据"""  
      token = None
     
   
  def replace_data(data):
     # 判断是否有需要替换的数据
      while re.search("#(.+?)#", data):  # .匹配任意字符,+重复一次或多次,?匹配0次或1次(变成非贪婪模式)()匹配分组:在匹配的数据中提取数据
          key = re.search("#(.+?)#", data).group(0) # 字典的键
          value = re.search("#(.+?)#", data).group(1)  # 字典的值
          try:
              data = data.replace(key, conf.get('test_info', value))  # 替换信息为conf.ini中的值
          except:
              data = data.replace(key,getattr(TestData,value)) # 如果满足条件,且配置文件中没有配置数据,则读取临时变量的值
      return data


TAG: 用例设计

 

评分:0

我来说两句

Open Toolbar