Py学习  »  Python

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

51Testing软件测试网 • 4 年前 • 710 次点击  


目录

 

 1、开发环境

 2、用到的模块

 3、框架设计

 3.1、流程

 3.2、项目结构

 4、日志打印

 5、接口请求类封装

 6、Excel数据读取

 6.1、读取配置文件

 6.2、编写Excel操作类

 7、用例组装

 8、用例运行结果校验

 9、运行用例

 10、小结


1、开发环境

  • 操作系统:Ubuntu18

  • 开发工具:IDEA+PyCharm插件

  • Python版本:3.6


2、用到的模块

  • requests:用于发送请求

  • xlrd:操作Excel,组织测试用例

  • smtplib,email:发送测试报告

  • logging:日志追踪

  • json:数据格式化

  • Django:接口开发

  • configparser:读取配置文件


3、框架设计

  3.1、流程

  接口用例是通过Excel来组织的,定义了URL,Request Body等列。执行流程如下:

  • 使用xlrd工具读取Excel中的信息,拼接成一个个完整的请求。

  • 接口请求类拿到一个个完整的请求的去执行,这个过程需要记录日志,每一次执行情况都要有迹可循。

  • 回填测试结果,发送邮件,归档每次的运行结果。更好一点的做法是把历史运行情况做个报表,更直观。

  优点:

  • 用例通过Excel来组织,不需要编写代码,上手难度小。

  • 在用例个数不是很多的情况,开发速度快。

  缺点:

  • 用例依赖是痛点。

  • 只能支持接口自动化用例。

  • Excel中用例无法预先检查正确性,只有跑一把才能知道。

  • 无法很好地管理大量用例,且不支持团队协同作业,个人用来回归测试或上线后的冒烟测试会是一个不错的选择。

  通过优缺点的对比,可以明显发现这个框架的硬伤其实很多了。所以无论是业界开源的自动化测试框架或企业自研的还没有见过用Excel来组织用例的。值得一提的是个别企业自研的自动化框架非常难用,抑或是把一堆工具简单组合到一起。根本无法提高团队的生产力。不过好的产品也不是一蹴而就的,需要一个持续优化的过程。所以上面用Excel组织用例的框架还是值的玩一玩的,暂且命名为apitest吧。目前比较好的自动化测试框架有unittest,testng,pytest等。

  3.2、项目结构

  testcase:存放测试用例或请求的json文件。

  config:配置文件。

  report:测试报告和日志文件及其归档。

  untils:工具集,send_request用来发送请求,email_tool用来发送邮件,excel_tool用来读取Excel中的数据,check_result用来校验结果,run_main用例执行入口,log_trace用来追踪日志。


4、日志打印


  采用内置logging模块才记录运行日志,设置日志级别。

  log_trace.log:

import  logging

filename = "../report/test_case_run.log"

logging.basicConfig(level=logging.INFO,

format='%(asctime)s %(levelname)s1 %(filename)s [line:%(lineno)d]  %(message)s',

datefmt='%a, %d %b %Y %H:%M:%S',

filename=filename,

filemode='w')


5、接口请求类封装


   安装第三方模块requests

pip install requests

  定义函数send_request,根据传入的方法类型分别去调用request的get,post,delete,put等方法去发送请求。send_request.py:

import  requests

from untils. log_trace import  *

#发送get请求

def get_request(url,data=None,headers=None):

res = requests.get(url=url,data=data,headers=headers)

return res

#发送post请求

def post_request(url,data,headers=None):

res = requests.post(url=url,data=data,headers=headers)

return res

#发送delete请求

def del_request(url,data=None,headers=None):

res = requests.delete(url,data=data)

return res

#发送put请求

def put_request(url,data,headers=None):

pass

def send_request(method,url,data=None,headers=None):

try:

logging.info(headers)

if headers:

if method == "GET":

return get_request(url,data,headers=headers)

if method == "POST":

return post_request(url,data=data,headers=headers)

if method == "DELETE":

return  del_request(url,data=data,headers=headers)

#put使用频率低,暂时不写

if method == "PUT":

return  put_request(url,data=data,headers=headers)

else:

logging.info("Header is null")

except Exception as e:

logging.info("send request fail:%s"%e)

  在untils_test.py中编写代码测试send_request方法,代码如下:

#coding:utf-8

from untils.send_request import send_request

def test_send_request():

url="http://127.0.0.1:9000/articles/"

headers = {

"X-Token":"0a6db4e59c7fff2b2b94a297e2e5632e"

}

res = send_request("GET",url,headers=headers)

print(res.json())

if __name__ == "__main__":

test_send_request()

  运行结果:

/usr/bin/python3.6 /home/stephen/IdeaProjects/apitest/untils/untils_test.py

{'status': 'BS.200', 'all_titles': {'amy1': 'alive', 'modifytest': 'alive', 'addTest': 'alive'}, 'msg': 'query articles sucess.'}

Process finished with exit code 0


6、Excel数据读取


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

pip install xlrd

pip install xluntils

  6.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")

  6.2 编写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)


7、用例组装


  有了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("-----------------------------------------------------------")


……


点击阅读原文全文


推荐阅读

点击阅读☞测试人员不得不小心那些职场套路

点击阅读☞接口测试框架实战与自动化进阶指南!

点击阅读☞教你如何快速搭建数据驱动自动化测试框架?

点击阅读☞从0到1 | 搭建自动化测试框架的思路及实例演示

点击阅读☞基于AI的移动端自动化测试框架的设计与实践

上文内容不用于商业目的,如涉及知识产权问题,请联系小编(021-64471599-8017)。



“阅读原文”查看全文!
爱我请给我好看!
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/32973
 
710 次点击