接口api自动化测试python_自动化测试接口PYTHON

在开发测试中经常会遇到接口迭代和代码重构,一个无关紧要的改动往往会引起整个项目的运行。现有的接口测试中往往只是针对单一接口的测试,可是业务的连贯性是非常紧密的,比如:用户从登陆,获取商品信息,下单,支付回调的处理,以及退款等流程的处理。发现没有好使的接口测试工具(jmeter太难用了)。于是自己使用python 实现自动化接口测试的一套脚本,该脚本可以实现单一接口的测试,和流程的测试;支持多个项目之间的测试;主要的功能如下:

读取Excel

接口参数(1、地址栏参数,2、body参数)动态化,可以从请求返回值中提取数据作为全局参数,供流程下一步骤使用

根据接口发送请求

结果的诊断,使用jsonpath

结果报告邮件

#!/usr/bin/python

# -*- coding: UTF-8 -*-

import xlrd

import requests

import json

import logging

import smtplib

from email.mime.text import MIMEText

from email.utils import formataddr

import jsonpath

import sys

import traceback

#日志定义

logging.basicConfig(level=logging.DEBUG, # log level

format='[%(asctime)s] %(levelname)s [%(funcName)s: %(filename)s, %(lineno)d] %(message)s', # log格式

datefmt='%Y-%m-%d %H:%M:%S', # 日期格式

filename='log.txt', # 日志输出文件

filemode='a') # 追加模式

class Config:

global PHP_API_DOMAIN #php接口域名

global JAVA_API_DOMAIN #java接口域名

global TEST_CASE_PATH #测试用例excel的路径

global TEST_CASE_LIST #测试用例转换后的集合<字典>

global PARAMS #存储结果变量

global CURRENT_REQUEST_HEADER #存储当前请求的请求头

global CURRENT_REQUEST_PARAM #存储当前请求的请求数据

global ALL_REQUEST_RESULT

global CURRENT_REQUEST_STATUS

def __init__(self,php_api_domain,java_api_domain,test_case_path):

self.PHP_API_DOMAIN = php_api_domain

self.TEST_CASE_PATH = test_case_path

self.JAVA_API_DOMAIN = java_api_domain

self.TEST_CASE_LIST = []

self.PARAMS = {}

self.CURRENT_REQUEST_HEADER = {}

self.CURRENT_REQUEST_PARAM = {}

self.ALL_REQUEST_RESULT = []

self.CURRENT_REQUEST_STATUS = True

headers = {'Accept': 'application/json, text/javascript, */*',

'Accept-Encoding':'gzip, deflate, br',

'Accept-Language':'zh-CN,zh;q=0.9',

'Connection':'Keep-Alive',

'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'}

class request_handler:

test_case = {}

result_obj = {}

assert_obj = {}

assert_result = True

need_assert = True

def __init__(self, test_case):

self.test_case = test_case

self.main()

self.return_entity()

def main(self):

self.request(self.test_case)

if self.need_assert:

self.setting_param()

def return_entity(self):

return_entity = {}

return_entity['成功状态'] = self.assert_result

return_entity['请求结果数据'] = self.result_obj

return_entity['断言结果'] = self.assert_obj

return_entity['用例数据'] = self.test_case

return_entity['请求参数'] = config.CURRENT_REQUEST_PARAM

return_entity['请求头'] = config.CURRENT_REQUEST_HEADER

logging.info(return_entity)

if not self.assert_result:

config.ALL_REQUEST_RESULT.append(return_entity)

config.CURRENT_REQUEST_STATUS = False

else:

config.CURRENT_REQUEST_STATUS = True

def request(self,test_case):

try:

if test_case['method'] == 'GET':

self.result_obj = self.request_get(test_case)

if test_case['method'] == 'POST':

self.result_obj = self.request_post(test_case)

if test_case['method'] == 'DELETE':

self.result_obj = self.request_delete(test_case)

if test_case['method'] == 'PUT':

self.result_obj = self.request_put(test_case)

self.assert_handler()

except Exception as e:

self.need_assert = False

self.assert_result = False

self.result_obj = {"status_code": "error", "error_message": e}

traceback.print_exc()

info = traceback.format_exc()

print(info)

def request_get(self,object):

url = self.get_url(object['url'], object['type'], object['param_data'])

head = headers

if isinstance(object['header'],list):

head = self.get_config_param(object['header'], head)

request_body = {}

if isinstance(json.loads(object['body_data']),dict):

request_body = self.get_config_param(json.loads(object['body_data']), request_body)

print("GET URL:"+url)

config.CURRENT_REQUEST_HEADER = head

config.CURRENT_REQUEST_PARAM = request_body

response = requests.get(url=url, params=request_body,headers=head)

return {"status_code" : response.status_code, "response_content": response.text}

def request_delete(self,object):

url = self.get_url(object['url'], object['type'], object['param_data'])

head = headers

if isinstance(object['header'],list):

head = self.get_config_param(object['header'], head)

request_body = {}

if isinstance(json.loads(object['body_data']),dict):

request_body = self.get_config_param(json.loads(object['body_data']), request_body)

print("GET URL:"+url)

config.CURRENT_REQUEST_HEADER = head

config.CURRENT_REQUEST_PARAM = request_body

response = requests.get(url=url, params=request_body,headers=head)

return {"status_code" : response.status_code, "response_content": response.text}

def request_put(self,object):

url = self.get_url(object['url'], object['type'], object['param_data'])

head = headers

if isinstance(object['header'],list):

head = self.get_config_param(object['header'], head)

request_body = {}

if isinstance(json.loads(object['body_data']),dict):

request_body = self.get_config_param(json.loads(object['body_data']), request_body)

print("GET URL:"+url + '; 参数:' + request_body)

config.CURRENT_REQUEST_HEADER = head

config.CURRENT_REQUEST_PARAM = request_body

response = requests.get(url=url, params=request_body,headers=head)

return {"status_code" : response.status_code, "response_content": response.text}

def request_post(self,object):

head = headers

#将header中的数据添加到请求中

if isinstance(json.loads(object['header']),dict):

head = self.get_config_param(json.loads(object['header']),head)

url = self.get_url(object['url'], object['type'], object['param_data'])

#将data中的动态参数从共享参数中添加到数据中

request_body = {}

if isinstance(json.loads(object['body_data']),dict):

request_body = self.get_config_param(json.loads(object['body_data']), request_body)

print("POST URL:" + url+";param:"+str(request_body))

config.CURRENT_REQUEST_HEADER = head

config.CURRENT_REQUEST_PARAM = request_body

response = requests.post(url, request_body, headers=head)

return {"status_code": response.status_code, "response_content": response.text}

def get_config_param(self, list, object):

for param_name in list.keys():

param_value = list[param_name]

if not str(param_value).startswith("$P_"):

object[param_name] = param_value

else:

body_value = config.PARAMS[param_value] if param_value in config.PARAMS.keys() else param_value

object[param_name] = body_value

return object

def get_url(self, path, type, param_data):

request_url = (config.PHP_API_DOMAIN + '/' + path) if type == 'php' else (config.JAVA_API_DOMAIN + '/' + path)

param_data = json.loads(param_data)

url_param = self.get_config_param(param_data, {})

request_url = request_url + '?a=a'

for key in url_param.keys():

request_url = request_url + "&" + key + '=' + url_param[key]

return request_url

def assert_handler(self):

test_case = self.test_case

result_obj = self.result_obj

self.assert_obj = {"测试用例名称:": test_case['case_name']}

# 访问接口直接报错直接

if result_obj['status_code'] == 'error':

self.assert_obj = dict(self.assert_obj.items(), {"请求状态:": "错误", "错误信息:": result_obj['error_message']}.items())

self.need_assert = False

self.assert_result = False

return

# 状态大于300的异常直接处理

if result_obj['status_code'] >= 300:

self.assert_obj['请求状态'] = result_obj['status_code']

self.assert_obj['错误信息'] = result_obj['response_content']

self.need_assert = False

self.assert_result = False

return

# 请求状态为成功,判断业务状态

buiess_content = json.loads(result_obj['response_content'])

expect_res = json.loads(test_case['expect_res'])

# 校验规则

for ruler in expect_res:

matcher = jsonpath.jsonpath(buiess_content, ruler['rule'])

if isinstance(matcher,bool):

self.assert_obj['请求状态'] = result_obj['status_code']

self.assert_obj['错误信息'] = '规则:' + ruler['rule'] + '值不匹配,期望是:' + str(ruler['value']) + ',返回是:' + str(

matcher)

self.assert_result = False

break

elif len(matcher) == 0:

self.assert_obj['请求状态'] = result_obj['status_code']

self.assert_obj['错误信息'] = '规则:' + ruler['rule'] + '提取出的数组长度为0'

self.assert_result = False

break

elif ruler['type'] == 'value': # 对值进行比较

if matcher[0] == ruler['value']:

continue

else:

self.assert_obj['请求状态'] = result_obj['status_code']

self.assert_obj['错误信息'] = '规则:' + ruler['rule'] + '值不匹配,期望是:' + str(ruler['value']) + ',返回是:' + str(

matcher[0])

self.assert_result = False

break

elif ruler['type'] == 'length': # 对数组长度进行比较

if len(matcher) == ruler['value']:

continue

else:

self.assert_obj['请求状态'] = result_obj['status_code']

self.assert_obj['错误信息'] = '规则:' + ruler['rule'] + '大小不匹配,期望是:' + str(ruler['value']) + ',返回是:' + str(

len(matcher))

self.assert_result = False

break

else:

self.assert_obj['请求状态'] = result_obj['status_code']

self.assert_obj['错误信息'] = '规则:' + ruler['rule'] + '错误'

self.assert_result = False

break

def setting_param(self):

actives = json.loads(self.test_case['active'])

buiess_content = json.loads(self.result_obj['response_content'])

for active in actives:

try:

p_name = active['p_name']

p_value = jsonpath.jsonpath(buiess_content, active['p_value'])

if isinstance(p_value, bool):

p_value = ''

elif len(p_value) == 0:

p_value = ''

else:

p_value = p_value[0]

config.PARAMS[p_name] = p_value

except Exception as e:

traceback.print_exc()

info = traceback.format_exc()

print(info)

def read_excel():

wb = xlrd.open_workbook(config.TEST_CASE_PATH)

sh = wb.sheet_by_name('Sheet1')

#处理 Excel的数据

for row_ndex in range(sh.nrows):

test_case = dict(zip(sh.row_values(0), sh.row_values(row_ndex)))

try:

if row_ndex == 0:

continue

request_handler(test_case)

if test_case['error_continue'] == 0 and not config.CURRENT_REQUEST_STATUS:

break

except Exception as e:

traceback.print_exc()

info = traceback.format_exc()

print(info)

#config.ALL_REQUEST_RESULT.append({"测试用例": test_case, "请求异常": e})

#发送邮件通知成功与否

message = ''

for result_record in config.ALL_REQUEST_RESULT:

message += json.dumps(result_record,ensure_ascii=False) +'\n\n\n'

send_email(message)

def send_email(message):

if len(message) == 0:

return

my_sender = 'xxx@xxx.com' # 发件人邮箱账号

my_pass = 'xxxxxx' # 发件人邮箱密码

my_user = 'xxx@xxx.com' # 收件人邮箱账号,我这边发送给自己

ret = True

try:

msg = MIMEText(message, 'plain', 'utf-8')

msg['From'] = formataddr(["小课接口测试报告", my_sender]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号

msg['To'] = formataddr(["250", my_user]) # 括号里的对应收件人邮箱昵称、收件人邮箱账号

msg['Subject'] = "小课接口测试报告" # 邮件的主题,也可以说是标题

server = smtplib.SMTP_SSL("imap.exmail.qq.com", 465) # 发件人邮箱中的SMTP服务器,端口是25

server.login(my_sender, my_pass) # 括号中对应的是发件人邮箱账号、邮箱密码

server.sendmail(my_sender, [my_user, ], msg.as_string()) # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件

server.quit() # 关闭连接

except Exception as e: # 如果 try 中的语句没有执行,则会执行下面的 ret=False

traceback.print_exc()

info = traceback.format_exc()

print(info)

ret = False

if ret:

print("发送邮件成功")

else:

print('发送邮件失败')

#config = Config(r'php域名', r'java域名',r'excel路径.xlsx')

if __name__ == '__main__':

argv = sys.argv

if len(sys.argv) != 4:

print('参数个数不正确')

php_api = argv[1]

java_api = argv[2]

excel_path = argv[3]

config = Config(php_api, java_api, excel_path)

read_excel()

EXCEL模板案列

case_name

header

type

url

method

param_data

body_data

execp_res

memo

active

error_continue

获取用户token

{}

java

student-service/get-student-token

GET

{"student_id":25009}

{}

[{"rule":"$.code","type":"value","value":0}]

[{"p_name":"$P_student_id","p_type":"int",

"p_value":"$.data.student_id"},{"p_name":"$P_token","p_type":"string",

"p_value":"$.data.token"}]

1

获取商品信息

{"token":"$P_token"}

php

product/info

POST

{}

{"product_id":3100052}

[{"rule":"$.code","type":"value","value":0}]

[{"p_name":"$P_product_id",

"p_value":"$.data.id"},{"p_name":"$P_course_id",

"p_value":"$.data.course_id"},{"p_name":"$P_total_money",

"p_value":"$.data.total_money"},{"p_name":"$P_actual_money",

"p_value":"$.data.actual_money"}]

0

下单

{"token":"$P_token"}

php

order/create

POST

{}

{"product_id":"$P_product_id","student_id":"$P_student_id",

"course_id":"$P_course_id",

"total_money":"$P_total_money",

"actual_money":"$P_actual_money","group_buy_order_id":null}

[{"rule":"$.code","type":"value","value":0}]

[{"p_name":"$P_order_id",

"p_value":"$.data.order_id"}]

1

1158536-20190321104705738-1381457706.png

字段说明:

case_name:测试用例名称

type:接口提供者(java/php)

url:请求地址path

method:请求接口类型(POST/GET/PUT/DELETE)

param_data:请求参数(需要放在地址栏上的参数),格式json

body_data:请求接口类型不是GET时,请求的body参数

expect_res:对接口返回数据的断言 ;rule:用jsonpath提取返回字段,type:value/length 标识判断字段值的长度还是值匹配,value:如果type == value,则判断返回字段值和value值是否相同;type==length 判断返回的数组或字符串长度是否相同

[{"rule":"$.code","type":"value","value":0},{"rule":"$.code","type":"length","value":1}]

active:将返回的数据值保存,作为下一步骤的请求参数,如下含义案例的含义是:存储返回值的json中的data下的student_id的值,使用时用 $P_student_id参数使用

[{"p_name":"$P_student_id","p_type":"int","p_value":"$.data.student_id"}]

error_continue:当前步骤诊断错误时,是否继续执行后面步骤

案列说明:

上述excel流程是,获取用户token ---》请求商品参数 ----》从商品中获取参数 ----》创建订单 流程,当然还有取消,支付,退款就不一一演示了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值