上一篇:Python3简易接口自动化测试框架设计与实现(上)

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/webDepOfQWS/,谢谢合作。

Python3简易接口自动化测试框架设计与实现(中)的更多相关文章

  1. Python3简易接口自动化测试框架设计与实现(上)

    目录 1.开发环境 2.用到的模块 3.框架设计 3.1.流程 3.2.项目结构 5.日志打印 6.接口请求类封装 接口开发请参考:使用Django开发简单接口:文章增删改查 1.开发环境 操作系统: ...

  2. 【python3+request】python3+requests接口自动化测试框架实例详解教程

    转自:https://my.oschina.net/u/3041656/blog/820023 [python3+request]python3+requests接口自动化测试框架实例详解教程 前段时 ...

  3. python 做接口自动化测试框架设计

    1,明确什么叫自动化测试,什么叫接口自动化测试,如何设计接口测试用例,已登录为例 自动化测试:解放人力来自动完成规定的测试. 自动化测试分层模型:UI层,不论WEB端还是移动端,都是基于页面元素的识别 ...

  4. 从接口自动化测试框架设计到开发(二)操作json文件、重构json工具类

    用例模板里的请求数据多,看起来很乱,所以可以通过访问另外一个文件的方式获取请求数据 把请求数据都放在一个json文件中 取出login的内容: import json fp = open('G:/un ...

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

    第1章 课程介绍(不要错过)本章主要讲解课程的详细安排.课程学习要求.课程面向用户等,让大家很直观的对课程有整体认知! 第2章 接口测试工具Fiddler的运用本章重点讲解如何抓app\web的htt ...

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

    引言 很多人都知道,目前市场上很多自动化测试工具,比如:Jmeter,Postman,TestLink等,还有一些自动化测试平台,那为啥还要开发接口自动化测试框架呢?相同之处就不说了,先说一下工具的局 ...

  7. 接口自动化 基于python实现的http+json协议接口自动化测试框架源码(实用改进版)

    基于python实现的http+json协议接口自动化测试框架(实用改进版)   by:授客 QQ:1033553122 欢迎加入软件性能测试交流QQ群:7156436     目录 1.      ...

  8. Maven+TestNG+ReportNG/Allure接口自动化测试框架初探(上)

    转载:http://www.51testing.com/html/58/n-3721258.html 由于一直忙于功能和性能测试,接口自动化测试框架改造的工作被耽搁了好久.近期闲暇一些,可以来做点有意 ...

  9. python3+request接口自动化框架

    首次书写博客,记录下写的自动化接口框架,框架比较简单,哈哈哈,算是记录下历程把!~~~ 一.本次框架由python3.6 书写 1.准备代码环境,下载python3.6    下载地址:https:/ ...

随机推荐

  1. 硬核讲解 Jetpack 之 LifeCycle 源码篇

    前一篇 硬核讲解 Jetpack 之 LifeCycle 使用篇 主要介绍了 LifeCycle 存在的意义,基本和进阶的使用方法.今天话不多说,直接开始撸源码. 本文基于我手里的 android_9 ...

  2. Hadoop常用命令介绍

    本文主要介绍 Hadoop 常用的命令. test命令 用于检测文件或目录是否存在,判断文件或目录大小. -test -[defsz] <path> : Answer various qu ...

  3. word内存不足 解决办法

    word2013内存不足 解决办法具体操作如下: 尝试了很多方法和经验表示都没有效果,最后找到个有效果的分享给大家: 点击“文件”——选项——加载项“   点左下的”管理“-” 转到“ 把加载项前面的 ...

  4. Entity Framework链接数据库设置

    本人不才,学习EntityFramwork同时做个记录供大家参考.不多说,直接上步骤 1.在WebConfig中添加如下代码段 <connectionStrings> <add na ...

  5. 在HTML5 中使用 kindeditor 的方法

    1.打开:http://kindeditor.net/ke4/examples/default.html 2.查看源代码,另存为 3.打开http://kindeditor.net/demo.php, ...

  6. nginx+uwsgi02---django部署(不推荐)

    1.文件结构 myweb/ ├── manage.py ├── myweb/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi. ...

  7. 【AtCoder】AGC009

    AGC009 A - Multiple Array 从后往前递推即可 #include <bits/stdc++.h> #define fi first #define se second ...

  8. c# dateTime格式转换为Unix时间戳工具类

    using System; using System.Collections.Generic; using System.Text; namespace TJCFinanceWriteOff.BizL ...

  9. windows下memcache扩展安装和搭建

    ### windows下memcache扩展安装和搭建 背景:在做微信公众号的开发时,token的有效期为7200秒,所以需要对token进行保存,在这选择了memcache作为缓存工具 memcac ...

  10. Python基础 第四章 字典(2)字典方法&章小结

    1. clear 方法clear删除所有的字典项,就地执行,什么都不返回(或者说返回None) d = {} d['name'] = 'Gumby' d['age'] = 42 print(d) re ...