Python接口自动化测试从设计到开发

7、Excel数据读取

用例是放在Excel中的,用xlrd来读取数据,写数据需要用到xluntils,先安装:

pip install xlrd

pip install xluntils

7.1、读取配置文件

读取Excel数据,我们需要知道对应的行和列,列相对固定,在配置文件settings中定义,然后读取,行作为参数传入。conf/settings文件中的定义如下:

[excel]
case_no=0
case_name=1
is_run=2
case_level=3
case_header=4
case_cookies=5
req_type=6
case_url=7
case_body=8
expect_result=9
operator=10
actual_result=11
test_result=12

在unitls/load_conf.py中编写读取配置的方法,获取各项列值的方法。lood_conf()函数需要传入两个参数:配置项字符串标识符,配置项类型。比如要读取excel下整数case_url:lood_conf("excel.case_url","int")。class excel_config()下定义返回各项列值的方法。

完整代码如下:

import  configparser

‘‘‘
read conf from setting.conf
@:parameter:identstr,value_type
value_type:"int" or "str"
‘‘‘
def lood_conf(identstr,value_type):
    cf = configparser.ConfigParser()
    cf.read("../config/settings.conf")

    idenlist = identstr.split(‘.‘)

    if value_type == "int":
        try:
         value = cf.getint(idenlist[0],idenlist[1])
         return  value
        except (configparser.NoSectionError ,configparser.NoOptionError) as e:
            print(e)
    if value_type == "str":
        try:
            value = cf.get(idenlist[0],idenlist[1])
            return value
        except (configparser.NoSectionError ,configparser.NoOptionError) as e:
            print(e)

‘‘‘
获取url,request body等的列号
‘‘‘
class excel_config():
    #获取用例编号的列
    def caseno_col(self):
        return lood_conf("excel.case_no","int")

    def casename_col(self):
        return lood_conf("excel.case_name","int")

    def isrun_col(self):
        #print(lood_conf("excel.is_run","int"))
        return lood_conf("excel.is_run","int")

    def level_col(self):
        return lood_conf("excel.case_level","int")

    def header_col(self):
        return lood_conf("excel.case_header","int")

    def cookies_col(self):
        return lood_conf("excel.case_cookies","int")

    def reqtype_col(self):
        return lood_conf("excel.req_type","int")

    def caseurl_col(self):
        return lood_conf("excel.case_url","int")

    def casebody_col(self):
        return lood_conf("excel.case_body","int")

    def expectresult_col(self):
        return lood_conf("excel.expect_result","int")

    def actualresult_col(self):
        return lood_conf("excel.actual_result","int")

    def testresult_col(self):
        return lood_conf("excel.test_result","int")

    def test_operator_col(self):
        return lood_conf("excel.operator","int")

7.1、编写Excel操作类

unitls/excel_tool.py中定义了获取用例编号,用例名称等方法,需要传入行。回写测试结果,回写实际结果方法需要传入两个参数:行,值。完整代码如下:

#coding:utf-8
import xlrd
from untils.log_trace import *
from xlutils.copy import copy
from untils.load_conf import excel_config

class excel_tool():

    def __init__(self,excel_name):

        self.curr_excel = xlrd.open_workbook(excel_name)
        self.table = self.curr_excel.sheet_by_index(0)
        #print(self.table.cell(1,1).value)
        #实例化excel_config
        self.config = excel_config()
        self.rows = self.table.nrows
        self.excel_name = excel_name

    #获取用例编号
    def get_caseno(self,row):
        caseno = self.table.cell(row,self.config.caseno_col()).value
        if caseno:
            return caseno
        else:
            logging.info("case no is null")
            return None

    #获取用例名称
    def get_casename(self,row):
        casename = self.table.cell(row,self.config.casename_col()).value
        return casename

    #获取是否运行标志
    def get_runflag(self,row):
        run_flag = self.table.cell(row,self.config.isrun_col()).value
        return run_flag

    #获取用例级别
    def get_caselevel(self,row):
        caselevel = self.table.cell(row,self.config.level_col()).value
        return caselevel

    #获取请求url
    def get_caseurl(self,row):
        caseurl = self.table.cell(row,self.config.caseurl_col()).value
        return caseurl

    #获取请求body
    def get_casebody(self,row):
        case_body = self.table.cell(row,self.config.casebody_col()).value
        return case_body

    #获取header
    def get_headerflag(self,row):
        headerflag = self.table.cell(row,self.config.header_col()).value
        return headerflag

    #获取coocikes
    def get_cookiesflag(self,row):
        cookiesflag = self.table.cell(row,self.config.cookies_col()).value
        return cookiesflag

    #获取请求类型
    def get_methodtype(self,row):
        method_type = self.table.cell(row,self.config.reqtype_col()).value
        return method_type
    #获取预期结果
    def get_expectres(self,row):
        expect_res = self.table.cell(row,self.config.expectresult_col()).value
        return expect_res

    #获取测试结果
    def get_testres(self,row):
        test_res= self.table.cell(row,self.config.testresult_col()).value
        return test_res
    #获取操作符
    def get_operator(self,row):
        operator = self.table.cell(row,self.config.test_operator_col()).value
        return operator

    #回写测试结果到excel
    def write_testres(self,row,value):
        wbook = copy(xlrd.open_workbook(self.excel_name))
        sheet = wbook.get_sheet(0)
        sheet.write(row, self.config.testresult_col(), value)
        wbook.save(self.excel_name)
    #回写实际结果
    def write_actualres(self,row,value):
        wbook = copy(xlrd.open_workbook(self.excel_name))
        sheet = wbook.get_sheet(0)
        sheet.write(row, self.config.actualresult_col(), value)
        wbook.save(self.excel_name)

8、用例组装

有了Excel操作类,就可以方便读取数据和回填结果了。接下来,在unitls/run_main.py中来组装用例。组装之前,先获取是否运行的标志:

  • 运行标志为N,不组装,将用例标记为skiiped,回填测试结果到Excel文件中。
  • 运行标志为Y,开始组装用例并执行,并对比预期结果和实际结果。
  • 用例执行通过,将用例标记为pass,回填测试结果和实际结果,实际结果为接口的返回。
  • 用例执行失败,将用例标记为failed,回填测试结果和实际结果。

接口鉴权需要用到的headers,先在run_main.py 中写死,这个问题后面解决,在上面的过程中,增加必要的日志,方便定位问题和查看用例的运行日志。完整代码如下:

#coding:utf-8
from untils.excel_tool import excel_tool
from untils.send_request import send_request
from untils.log_trace import *
from untils.check_result import CheckResult
import  json
headers = {
    "X-Token":"0a6db4e59c7fff2b2b94a297e2e5632e"
}

class runner():
    def __init__(self):
        self.excel = excel_tool("../testcase/test.xls")
        self.check = CheckResult()

    def join_case(self):
        global  skip_list,sucess_list,failed_list,skip_list
        sucess_list = []
        sucess_list = []
        failed_list = []
        skip_list = []

        for row in range(1,self.excel.rows):
            no = self.excel.get_caseno(row)
            url = self.excel.get_caseurl(row)
            isrun = self.excel.get_runflag(row)
            name = self.excel.get_casename(row)
            level = self.excel.get_caselevel(row)
            data = self.excel.get_casebody(row)
            expect_res = self.excel.get_expectres(row)
            method = self.excel.get_methodtype(row)
            hasheader = self.excel.get_headerflag(row)
            operator = self.excel.get_operator(row)

            if isrun == "Y":
                logging.info("Begin to run test case : %s,case number :%s" %(name,no))
                logging.info("Request method type is :%s" %method)
                logging.info("Request URL:%s" %url)
                logging.info("Request Body:%s" %json.dumps(json.loads(data),sort_keys=True,indent=2))
                res = send_request(method,url,data=data,headers=headers)

                is_sucess = self.check.cmpdict(eval(expect_res),eval(res.text),operator)
                print(is_sucess)
                if is_sucess:
                    sucess_list.append(name)
                    #回写测试结果
                    self.excel.write_testres(row,"pass")
                    #回写实际结果
                    self.excel.write_actualres(row,res.text)
                    logging.info("Test case %s run sucess." %name)
                else:
                    failed_list.append(name)
                    print("fail",is_sucess)
                    #回写测试结果
                    self.excel.write_testres(row,"failed")
                    #回写实际结果
                    self.excel.write_actualres(row,res.text)
                    logging.error("Test case %s run fail." %name)

                logging.info("Response is:%s" %json.dumps(res.json(),sort_keys=True,indent=2))

            else:
                skip_list.append(name)
                self.excel.write_testres(row,"skipped")

    def sum(self):

        total = len(sucess_list)+len(failed_list) + len(skip_list)
        failed = len(failed_list)
        sucess = len(sucess_list)

        logging.info("-----------------------------------------------------------")
        logging.info("本次一共运行:%s 个用例" %total)
        logging.info("本次运行通过:%s 个用例" %sucess)
        logging.info("本次运行跳过:%s 个用例" %len(skip_list))
        logging.info("跳过的用例:%s" %skip_list)
        logging.info("-----------------------------------------------------------")

9、用例运行结果校验

在untils/run_main.py中方法cmpdict()是用来校验预期和结果实际结果是否匹配,需要传入三个参数:预期结果字典,实际结果字典,操作符。在check_result.py中编写校验用例结果的方法。目前只支持两种操作符,equal和notequal,预期结果为字典,其中不能嵌套字典。和完整代码如下:

from untils.log_trace import *
class  CheckResult():
    def dict_value(self,key,actual):
        try:
            if key in actual:
                return actual[key]
            else:
                for keys in actual:

                    return self.dict_value(key,actual[keys])
        except Exception as e:
            logging.error(e)
            return None

    def cmpdict(self,expect,actual,equal):
        logging.info("Begin to check result of  testcase.")
        is_dict = isinstance(expect,dict) and isinstance(actual,dict)
        if is_dict:
            if equal == "equal":
                for key in expect.keys():
                    if expect[key] == self.dict_value(key,actual):
                        logging.info("%s is equal to %s" %(expect[key],self.dict_value(key,actual)))
                        return True
                    else:
                        logging.error("%s is not equal to %s" %(expect[key],self.dict_value(key,actual)))
                        return False

            if equal == "notequal":
                for key in expect.keys():
                    if key != self.dict_value(key,actual):
                        logging.info("%s is not equal to %s" %(expect[key],self.dict_value(key,actual)))
                        return True
                    else:
                        logging.error("%s is equal to %s" %(expect[key],self.dict_value(key,actual)))
                        return False

            else:
                logging.error("Operator :%s is not support now,you can define it in file[check_result.py]" %equal)

        else:
            logging.error("Expect or actual  result is not dict,check it in  excel. ")

10、运行用例

新建一个名称为test.xls的Excel,将其放到testcase路径下,并在Excel中编写测试用例。接口开发请参考:使用Django开发简单接口:文章增删改查,我准备的用例如下:

在untils/untils_test.py中导入run_mian模块来测试一下:

from untils.run_main import runner
if __name__ == "__main__":
    #test_send_request()
    runner = runner()
    runner.join_case()
    runner.sum()

运行untils_test.py,然后去到Excel中查看运行结果:

report路径下查看测试用例运行日志,如下所示:

Sat, 11 May 2019 19:37:56 INFO check_result.py [line:16]  Begin to check result of  testcase.
Sat, 11 May 2019 19:37:56 ERROR check_result.py [line:38]  Operator :e1qual is not support now,you can define it in file[check_result.py]
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:37]  Begin to run test case : 查询文章,case number :1.0
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:38]  Request method type is :GET
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:39]  Request URL:http://127.0.0.1:9000/articles
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:40]  Request Body:{}
Sat, 11 May 2019 19:37:56 INFO send_request.py [line:25]  {‘X-Token‘: ‘0a6db4e59c7fff2b2b94a297e2e5632e‘}
Sat, 11 May 2019 19:37:56 INFO check_result.py [line:16]  Begin to check result of  testcase.
Sat, 11 May 2019 19:37:56 INFO check_result.py [line:22]  BS.200 is equal to BS.200
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:52]  Test case 查询文章 run sucess.
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:62]  Response is:{
  "all_titles": {
    "Hello": "alive",
    "amy1": "alive",
    "modifytest": "alive",
    "useasge of ddt": "alive"
  },
  "msg": "query articles sucess.",
  "status": "BS.200"
}
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:37]  Begin to run test case : 新增文章,case number :2.0
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:38]  Request method type is :POST
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:39]  Request URL:http://127.0.0.1:9000/articles/
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:40]  Request Body:{
  "content": "useasge of ddt",
  "title": "useasge of ddt"
}
Sat, 11 May 2019 19:37:56 INFO send_request.py [line:25]  {‘X-Token‘: ‘0a6db4e59c7fff2b2b94a297e2e5632e‘}
Sat, 11 May 2019 19:37:56 INFO check_result.py [line:16]  Begin to check result of  testcase.
Sat, 11 May 2019 19:37:56 ERROR check_result.py [line:25]  BS.200 is not equal to BS.400
Sat, 11 May 2019 19:37:56 ERROR run_main.py [line:60]  Test case 新增文章 run fail.
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:62]  Response is:{
  "msg": "title aleady exist,fail to publish.",
  "status": "BS.400"
}
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:37]  Begin to run test case : 修改文章,case number :3.0
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:38]  Request method type is :POST
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:39]  Request URL:http://127.0.0.1:9000/articles/7
Sat, 11 May 2019 19:37:56 INFO run_main.py [line:40]  Request Body:{
  "content": "modify test",
  "title": "modify test"
}
Sat, 11 May 2019 19:37:56 INFO send_request.py [line:25]  {‘X-Token‘: ‘0a6db4e59c7fff2b2b94a297e2e5632e‘}
Sat, 11 May 2019 19:37:57 INFO check_result.py [line:16]  Begin to check result of  testcase.
Sat, 11 May 2019 19:37:57 ERROR check_result.py [line:25]  BS.200 is not equal to BS.300
Sat, 11 May 2019 19:37:57 ERROR run_main.py [line:60]  Test case 修改文章 run fail.
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:62]  Response is:{
  "msg": "article is not exists,fail to modify.",
  "status": "BS.300"
}
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:37]  Begin to run test case : 删除文章,case number :4.0
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:38]  Request method type is :DELETE
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:39]  Request URL:http://127.0.0.1:9000/articles/7
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:40]  Request Body:{}
Sat, 11 May 2019 19:37:57 INFO send_request.py [line:25]  {‘X-Token‘: ‘0a6db4e59c7fff2b2b94a297e2e5632e‘}
Sat, 11 May 2019 19:37:57 INFO check_result.py [line:16]  Begin to check result of  testcase.
Sat, 11 May 2019 19:37:57 ERROR check_result.py [line:25]  BS.200 is not equal to BS.300
Sat, 11 May 2019 19:37:57 ERROR run_main.py [line:60]  Test case 删除文章 run fail.
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:62]  Response is:{
  "msg": "article is not exists,fail to delete.",
  "status": "BS.300"
}
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:74]  -----------------------------------------------------------
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:75]  本次一共运行:5 个用例
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:76]  本次运行通过:1 个用例
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:77]  本次运行跳过:1 个用例
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:78]  跳过的用例:[‘新增文章缺少title‘]
Sat, 11 May 2019 19:37:57 INFO run_main.py [line:79]  -----------------------------------------------------------

11 、小结

框架终于能跑起来了,但是遗留的问题还很多。

  • 很多地方的代码不够健壮,这个后面慢慢优化。还有用例校验支持的运算符比较少。
  • 发送邮件模块待完成。
  • Headers的问题如何解决?
  • 如果请求的body比较多,写在Excel是不是很不美观呀?这个可以从固定地方读取文件来完成。
  • Excel中测试用例有没有必填项呀?这个可以在运行结果之前进行校验,必填项缺少,不运行。
  • 最关键的一点,如果第二个用例依赖于第一个用例的返回,用例依赖一直是个痛点,下一篇解决。

原文地址:https://www.cnblogs.com/cs1188/p/11516946.html

时间: 2024-11-09 17:53:42

Python接口自动化测试从设计到开发的相关文章

从接口自动化测试框架设计到开发(五)--case运行结果统计、发送邮件

1.case运行结果统计 #Run_Test.py# -*- coding: utf-8 -*- # @Author: jiujiu # @Date: 2020-03-04 16:30:31 # @Last Modified time: 2020-03-07 10:05:52 import sys sys.path.append("G:/uni_test") #添加当前过程的目录 import json from base.run_method import RunMethod fro

Python接口自动化测试框架实战 从设计到开发

第1章 课程介绍(不要错过)本章主要讲解课程的详细安排.课程学习要求.课程面向用户等,让大家很直观的对课程有整体认知! 第2章 接口测试工具Fiddler的运用本章重点讲解如何抓app\web的http\https请求包.如何模拟请求数据.过滤规则及修改响应数据.如何解决无法抓包问题        以及fiddler接口测试,让大家能应用好工具! 第3章 Requests常见方法实战运用本章重点讲解 get\post请求测试.接口测试中结果处理.上传\下载文件接口测试.请求中header及coo

颠覆你的Python接口自动化测试,约吗?

课程 一直以来,我都很喜欢收集大家问的比较多的问题,然后一边回答一边整理成文.今天也不例外,只是由一问一答,变成了三问三答. [你问] 我们为什么要做接口测试? [我答] 之前在<测试路上你问我答>系列里写过一篇答疑,详见<接口测试的是什么和为什么?> [你问] Python 语言现在为什么这么火? [我答] 这个问题,其实我的回答未必全面,大家在简书或者任何一个技术论坛搜索一下,很多阐述.推荐下面这篇:Python 为何能坐稳 AI 时代头牌语言? [你问] 老师,我之前看了你写

基于Python接口自动化测试框架(初级篇)附源码

引言 很多人都知道,目前市场上很多自动化测试工具,比如:Jmeter,Postman,TestLink等,还有一些自动化测试平台,那为啥还要开发接口自动化测试框架呢?相同之处就不说了,先说一下工具的局限性: 1.测试数据不可控:    接口虽然是对业务逻辑.程序代码的测试,而实际上是对数据的测试,调用接口输入一批数据,通过断言代码验证接口返回的数据,整个过程围绕数据测试.    如果返回的数据不是固定的,是变化的,那么断言失败,就无法知道是接口程序错误引起的,还是数据变化引起的,所以就需要进行测

python接口自动化测试

之前在项目中搞了一套jmeter +jenkins + git + ant接口自动化测试框架,在项目中运行了大半年了,效果还不错, 最近搞了一套requests + unittest + ddt +pymysql + BeautifulReport的接口自动化测试框架, 测试用例在yaml文件中, 支持数据驱动.连数据库查询. 目前框架还不是特别完善,需要在后续的使用中一步步优化,目前尚未使用jenkins做集成, 后续结合jenkins做成持续集成.接口自动化测试框架地址:https://gi

python接口自动化测试 - openpyxl基本使用

前言 当你做接口自动化测试时,测试用例逐渐变多情况下,如果所有测试用例都通过代码管理将会使得代码十分臃肿,而且维护成本会很高: 所以我们一般会通过Excel去管理所有的测试用例,而openpyxl库提供了访问Excel的方法 openpyxl简单介绍 一个Python库,用于读取/写入Excel 2010  xlsx .xlsm .xltx .xltm 文件 不能操作 xls 文件 openpyxl简单概念 Workbook:excel工作表 Sheet:工作表中的一张表 Cell:其中的一个单

python接口自动化测试框架

环境:python3 + unittest + requests Excel管理测试用例, HTMLTestRunner生成测试报告 测试完成后邮件发送测试报告 jsonpath方式做预期结果数据处理,后期多样化处理 后期扩展,CI持续集成 发送邮件效果: 整体结构: common模块 class IsInstance: def get_instance(self, value, check): flag = None if isinstance(value, str): if check ==

Python3简易接口自动化测试框架设计与实现 实例2

目录 1.开发环境 2.用到的模块 3.框架设计 ?3.1.流程 3.2.项目结构 5.日志打印 6.接口请求类封装 7.Excel数据读取 7.1.读取配置文件 7.1.编写Excel操作类 8.用例组装 9.用例运行结果校验 10.运行用例 11 .小结 1.开发环境 操作系统:Ubuntu18 开发工具:IDEA+PyCharm插件 Python版本:3.6 2.用到的模块 requests:用于发送请求 xlrd:操作Excel,组织测试用例 smtplib,email:发送测试报告 l

python 接口自动化测试--框架整改(五)

代码结构: 目标架构: 1.用例分析器,自动根据接口的参数,类型生成测试用例 2.数据分析器,自动维护接口参数数据,动态数据自动生成,返回结果自动查询判断 3.核心调用引擎,分SOAP和HTTP两种,调用用例,返回接口调用的结果 4.报告输出,统计用例通过率,错误日志,数据库持久化,持续集成 目前实现功能: 核心调用引擎,数据分析器(demo) 1.用例结构(更新) excel分两个sheet,sheet1中存放用例,结构如下 sheet2中存放测试环境host,接口wsdl地址,接口方法间的映