搭建接口自动化测试框架的思路分析和技术点
Posted leo3notblv
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搭建接口自动化测试框架的思路分析和技术点相关的知识,希望对你有一定的参考价值。
背景
为什么要做自动化测试?
节省测试时间,项目有一定体量后回归测试等场景需要耗费大量时间和人力
为什么要做接口自动化测试?
相对于ui,接口较稳定,实现自动化比较方便,代码成型后,用例和代码维护也比较方便
一、拆解接口测试的过程和场景
1.分析接口请求有哪些内容
- l 请求方法 method
- l 请求地址url
- l 环境配置 ip
- l 前置操作
- l Request Body (数据类型)
- l 状态码
- l Response body
- l 断言
- l 后置操作
2. 两种接口测试场景
1) 对单个接口进行校验
接口字段的数据类型/长度/空值等检查点做校验,此场景大量不同数据请求同一个接口,适合做数据驱动(DDT),此场景主要解决批量读取用例数据,加载DDT,记录执行结果三个问题。
2) 对接口进行业务流的测试
此场景会涉及多个接口及数据的插入及清洗等
1分析业务关系
可从流程图,4+1视图,设计文档等入手
2梳理
例:启动指标
启动指标的条件,该指标应存在(delete_flag=0)且指标状态为inactive
为确保用例的独立性(解耦),测试流程应为:
1新建指标并返回指标id
2请求指标启动接口,启动新建的指标;断言接口返回success/数据库内指标状态刷新为active;由于指标启动过程平台建任务有耗时,此处需要设置等待。
3数据的清理,请求指标停止接口停止该指标任务,删除此指标相关数据(指标表/指标表达式表)
二、分析接口自动化测试活动
1.接口拉通--编写用例的准备
接口文档
Postman(灵活运用postman生成代码的功能)
参照文档使用postman请求,拉通接口
2.测试过程相关数据的管理和组织
1.用例编写
用例数据 用例编号,用例名,url,method,入参,预期结果
2.用例文件的格式,组织方式 excel / python文件/yaml
1使用excel表格编写用例,注意用例表字段的设计
优点:维护简单,修改方便,不需要会编程
缺点:程序需要使用相关模块(openpyxl/xlrd等)支持才可以读取,需要编写相关代码
例:
适用于单接口校验做数据驱动
适用于业务场景测试,配合正则表达式和反射
2使用python文件编写用例
使用面向对象和继承的方法编写用例数据
优点:python可直接读取,不需要中间层,可直接在开发环境编写
缺点:编写人员需要会python语法,理解面向对象和继承等,跨语言较麻烦
例
class Case: #此处可使用类属性放置所有用例实例共有的属性,如IP,但是IP这种环境配置还是建议放在config里,不然用例改环境太麻烦 host_ip="" def __init__(self,num,name,url,method,req_body,exp_result): self.num="" self.name="" self.url="" self.method="" self.req_body="" self.exp_result="" #将用例属性进行实例化传入,因为整个框架设计数据传递主要使用json,所以建议所有字符串都用双引号 #实例化用例后导入到其他模块使用就可以了 case_01=Case("01","case_01","/abc","post",""user":"001"",""status":"success"") #多个用例时,可使用list传递,适用DDT case_02=Case("01","case_01","/abc","post",""user":"001"",""status":"success"") case_03=Case("01","case_01","/abc","post",""user":"001"",""status":"success"") case_box=[case_01,case_02,case_03] #当然,也可以写个循环生成用例,case_box.append()循环往里加 class New_case(Case): def __init__(exp_code): super().__init__(num,name,url,method,req_body,exp_result) self.exp_code = 200 #使用继承可以方便添加新的用例属性
3使用yaml文件编写用例
使用面向对象和继承的方法编写用例数据
优点:跨语言优势强
缺点:编写人员需要会yaml语法,需要使用第三方模块支持才能读取数据
例
# yaml语法
# key: key1: value1,key2: value2 注意:冒号后有空格,字符串建议使用双引号
case_data_workbook:
workbook: "../data_of_cases/case2.xlsx"
worksheet: "Sheet1"
# 用例编写
case_01:
num: "01"
name: "case_01"
url: "/abc/o1"
req_body : \'"credentials": "123456","account":"admin"\'
3.环境配置config
1使用python类编写配置文件
优缺点同上,我们直接看例子
""" 建立一个类 配置项用类属性来写,方便其他模块导入使用 路径可以使用pathlib拼接 """ class CONFIG:
#环境host HOST = "http://www.xxx.com:8108"
#用例excel文件 WORKBOOK = "../data_of_cases/case2.xlsx"
#用例sheet WORKSHEET = "Sheet1"
#日志输出路径 LOG_PATH = "../test_logs/runlog_.text"
#打印日志等级 LOG_LEVEL = "ERROR"
#测试报告输出路径 REPORT_PATH = "../test_report/report.html"
2使用yaml编写配置文件
优缺点也同上,我们直接看例子
#使用yaml作为配置文件,比较通用,跨语言有优势
#用例存储文件路径,及sheet页名
#建议采用相对路径
case_data_workbook:
workbook: "../data_of_cases/case2.xlsx"
worksheet: "Sheet1"
#log日志输出位置
#打印日志的等级
#日志切分 可按时间切分,也可按大小,如 1 MB切分
log_path: "../test_logs/runlog.log"
log_level: "ERROR"
log_rotation: "12:00"
#测试报告输出位置
report_path: "../test_report/report.html"
#后台域名,结合环境
host: "http://www.xxx.com:8108"
#前台域名
front_host: "http://www.xxx.com:8107"
3测试过程中的需求
框架
请求方法
读取用例数据
读取环境配置
数据预处理和清洗
执行结果记录
测试报告输出
...
三、 编写自动化框架需要的模块及包,技术点
1框架:
unittest
优点:python自带,使用比较方便
缺点:定制化自由度较pytest低
关于unittest的使用和规则,请参考大神的博客unittest - 断浪狂刀忆年少 - 博客园 (cnblogs.com)
Pytest
优点:定制化自由度比较高,兼容unittest
缺点:需要下载才能使用,自由度太高,往往也会不知该做什么
2请求方法 request
3读取用例数据 openpyxl python-操作excel之openpyxl - 断浪狂刀忆年少 - 博客园 (cnblogs.com)
4读取环境配置 yaml/python文件,具体看前文
5日志记录 loguru / logging
loging
优点:可完全根据自己需求编写功能
缺点:功能基础,需要开发的地方比较多
关于logging的使用和内容,可以参考大神的博客python 日志 logging模块(详细解析)_天健胡马灵越鸟的博客-CSDN博客
loguru
优点:高度封装,省心
缺点:给你什么用什么,不过已经满足需求
关于loguru的使用和容,可以参考大神的博客Loguru — 最强大的 Python 日志记录器-Python 实用宝典 (pythondict.com)
6MySQL数据处理
这个没什么可说的,pymysql好用,需要注意的点时数据库的事务conn.commit()
基本的使用方法
- 1连接数据库
- conn = pymysql.connect()获取连接对象
- conn = pymysql.connect()获取连接对象
- 2获取游标
- cursor = conn.cursor()
- cursor = conn.cursor()
- 3使用sql语句
- cursor.excute(sql语句)
- cursor.excute(sql语句)
- 4获取查询结果
- cursor.fetchall(),,,元组套元组,一条数据就是一个元素
- cursor.fetchone(),,,取出元组
- cursor.fetchall(),,,元组套元组,一条数据就是一个元素
- 每次查询前初始化一个游标
- connect中的cursorclass参数,相当于conn.cursor中的cursor参数,读出一个列表嵌套字典结构数据
- conn = pymysql.connect(cursorclass=pymysql.cursors.DictCursor)
- 详细的使用方法可参考大神的博客
7测试报告
unittestreport
适用于unittest 一、安装和介绍 - unittestreport 使用文档
allure
适用于pytest Pytest测试框架(五):pytest + allure生成测试报告 - 测试开发小记 - 博客园 (cnblogs.com)
8技术点
python函数、面向对象、继承、反射、json、正则表达式、封装等、sql语法、unittest框架、requests、装饰器等
四、框架目录结构
下面我们来看整个项目的目录结构
使用unittest框架 excel表格管理用例 yaml配置文件
├─common #公共包 │ │ tool_of_log.py #日志模块 │ │ tool_of_mysql.py #数据库模块 │ │ tool_of_read_config.py #读取配置模块 │ │ tool_of_read_excel.py #表格数据处理 │ │ tool_of_requests.py #通用请求方法 │ │ __init__.py │ ├─data_of_cases #用例数据包 │ │ case1.xlsx #用例表格 │ │ case2.xlsx │ ├─data_of_config #配置文件包 │ │ config.py #python类型的配置文件 │ │ config.yaml #yaml类型配置文件 │ ├─run #测试框架主入口 │ │ main.py #主入口 │ │ __init__.py │ ├─setup #前置处理包,一些前置处理可以单抽出来,如获取cookie │ │ 000345.png │ │ new_account.py │ │ put_product.py │ │ uuid_imagecode.py │ │ __init__.py │ ├─test_logs #测试日志包 │ │ runlog.log #测试日志文件 │ ├─test_mainclass #测试主类 │ │ test_cla_and_meth.py #被测类和被测方法 │ │ test_new_account.py #场景测试 新增用户 │ │ test_product_upload.py #场景测试 上传产品 │ │ __init__.py │ └─test_report #测试报告 │ │history.json #测试报告相关json文件,自动生成的 │ │report_2022-09-06_23-57-26.html #测试报告文件
五、代码实例
common
日志模块
""" 记录日志模块 sink为日志输出目录 level为写入日志等级 建议日志使用log文件记录 """ from loguru import logger from common.tool_of_read_config import read_config_yaml # 这里是读取配置文件的日志输出位置 config = read_config_yaml("../data_of_config/config.yaml") logger.add(sink=config["log_path"], level="ERROR", encoding="utf-8")
数据库模块
""" 连接数据库模块 提供了三个方法 1游标的初始化 2执行查询sql语句并销毁游标,返回查询到的数据 3关闭数据库连接 """ import pymysql class Tool_of_mysql: def __init__(self, host, port, user, password, dbname): # connect中的cursorclass参数,相当于conn.cursor中的cursor参数,读出一个列表嵌套字典结构数据 self.conn = pymysql.connect(host=host, port=port, user=user, password=password, db=dbname,cursorclass=pymysql.cursors.DictCursor) self.cursor = self.conn.cursor() def cursor_init(self): # 执行一次SQL,游标会销毁,所以提供游标初始化方法 self.cursor = self.conn.cursor() def run_sql(self, sql): # 这里建议只使用select语句 self.cursor.execute(sql) # 提交事务 self.conn.commit() db_data = self.cursor.fetchone() self.cursor.close() # self.conn.close() return db_data def mysql_close(self): self.conn.close() if __name__ == \'__main__\': lemon = Tool_of_mysql(host="47.113.180.81", port=3306, user="lemon", password="lemon123", dbname="yami_shops") # lemon.cursor_init() data = lemon.run_sql("select user_phone,mobile_code from tz_sms_log limit 5") print(data)
读取配置文件模块
""" 读取yaml配置文件模块 """ import yaml def read_config_yaml(filepath): with open(filepath, encoding="utf-8") as f: config_data = yaml.safe_load(f) return config_data if __name__ == \'__main__\': print(read_config_yaml("../data_of_config/config.yaml"))
表格数据处理模块
""" 读取excel表格的模块 """ import openpyxl from openpyxl.worksheet.worksheet import Worksheet def read_xlsx_tool(filename, sheetname): workbook = openpyxl.load_workbook(filename) # 这里做了类型注解,方便ide给出代码联想提示 sheet: Worksheet = workbook[sheetname] data = list(sheet.values) title = data[0] lines = data[1:] cases = [dict(zip(title, row)) for row in lines] #循环 并通过zip组装字段名和字段值并放进一个dict里 # for row in lines: # cases.append(dict(zip(title, row))) return cases if __name__ == \'__main__\': s = read_xlsx_tool("../data_of_cases/case2.xlsx", "Sheet2") print(type(s)) print(s[0]) print(type(s[0]))
通用请求方法,这个不一定能用到
""" 封装了请求方法的模块 默认请求方法post 其实该模块可以不封装,直接写代码并不是很多,处理比较方便,封装后加了一层导入 """ import requests def request_api(method=\'post\', url=None, headers=None, json_data=None, body_data=None): res = requests.request(method=method, url=url, headers=headers, json=json_data, data=body_data) return res
data_of_config
yaml环境配置文件
#使用yaml作为配置文件,比较通用,跨语言有优势 #用例存储文件路径,及sheet页名 #建议采用相对路径 case_data_workbook: workbook: "../data_of_cases/case2.xlsx" worksheet: "Sheet1" #log日志输出位置 #打印日志的等级 #日志切分 可按时间切分,也可按大小,如 1 MB切分 log_path: "../test_logs/runlog.log" log_level: "ERROR" log_rotation: "12:00" #测试报告输出位置 report_path: "../test_report/report.html" #后台域名,结合环境 host: "http://www.xxx.com:8108" #前台域名 front_host: "http://www.xxx.com:8107"
run
测试框架主入口
""" 运行测试的主模块,同时生成日志,和测试报告 """ import unittest import time import unittestreport suit = unittest.defaultTestLoader.discover("../test_mainclass") runner = unittestreport.TestRunner(suit, filename=f"report_time.strftime(\'%Y-%m-%d_%H-%M-%S\').html", report_dir="../test_report", title="迭代10_接口自动化测试报告", tester="woody", desc="XX项目迭代10------", templates=2) #文件名拼接时间,每次运行都会生成一个报告文件 runner.run()
test_logs
生成的测试日志
建议安装插件 ideolog,pycharm可直接高亮显示日志
2022-09-06 23:57:26.096 | ERROR | test_cla_and_meth:test_:36 - 用例---case_004--->执行失败 ====>\'code\': 200, \'message\': \'登录成功\', \'token\': \'good-token\' != \'code\': 200, \'message\': \'登录失败\', \'token\': \'none\' - \'code\': 200, \'message\': \'登录成功\', \'token\': \'good-token\' ? ^^ ^ ^^^^^^ - + \'code\': 200, \'message\': \'登录失败\', \'token\': \'none\' ? ^^ ^ ^ 2022-09-06 23:57:26.107 | ERROR | test_cla_and_meth:test_:36 - 用例---case_005--->执行失败 ====>\'code\': 200, \'message\': \'登录成功\', \'token\': \'good-token\' != \'code\': 200, \'message\': \'登录失败\', \'token\': \'none\' - \'code\': 200, \'message\': \'登录成功\', \'token\': \'good-token\' ? ^^ ^ ^^^^^^ - + \'code\': 200, \'message\': \'登录失败\', \'token\': \'none\' ? ^^ ^ ^ 2022-09-19 23:53:30.900 | ERROR | test_cla_and_meth:test_:39 - 用例---case_001--->执行失败 ====>\'"code":200,"message":"登录成功","token":"good-token"\' != <Request [post]>
test_mainclass
被测类和被测方法
""" 编写测试类和测试方法 使用unittest框架,导入ddt和list_data两个装饰器,分别装饰测试类和测试方法 导入公共层表格读取模块,读取用例数据 导入公共层配置文件读取模块,读取配置文件 导入公共层日志记录器 导入被测方法/函数 导入json模块处理数据 """ import unittest import requests from unittestreport import ddt, list_data from common.tool_of_read_config import read_config_yaml from common.tool_of_read_excel import read_xlsx_tool from common.tool_of_log import logger import json config = read_config_yaml("../data_of_config/config.yaml") host = config["host"] cases = read_xlsx_tool(filename=config["case_data_workbook"]["workbook"], sheetname=config["case_data_workbook"]["worksheet"]) # cases = read_xlsx_tool() @ddt class Test_cases(unittest.TestCase): @list_data(cases[0:1]) # 这里必须传入一个列表,所以定位单个用例,不能直接用索引,要用切片[],切出一个只有一个元素的列表 def test_(self, case): # 注意这里的“json=“参数,表格读出来是字符串,要转成Json才能做请求入参 # 这里的url是拼接的 response = requests.request(method=case["method"], url=host + case["url"], json=json.loads(case["入参"])) # 这里使用try尝试取结果,返回类型不同使用不同的方法 try: act_res = response.json() except Exception as e: res = response.text act_res = "msg": res expect = json.loads(case["预期结果"]) # try进行断言是为了写日志,再手动抛出异常 for k, v in expect.items(): try: self.assertEqual(v, act_res[k]) except AssertionError as e: logger.error(f\'e\') raise e # self.assertIn(case["预期结果"], res) # print(res) # expect_res = json.loads(case["预期结果"]) # 转成字典 # try: # # self.assertEqual(expect_res, res) # except AssertionError as e: # # logger.error(f"用例---case[\'用例名\']--->执行失败") # logger.error(f"用例---case[\'用例名\']--->执行失败 ====>e") # raise e
使用反射和正则表达式匹配测试业务场景
""" 编写测试类和测试方法 使用unittest框架,导入ddt和list_data两个装饰器,分别装饰测试类和测试方法 导入公共层表格读取模块,读取用例数据 导入公共层配置文件读取模块,读取配置文件 导入公共层日志记录器 导入被测方法/函数 导入json模块处理数据 """ import re import unittest import requests from unittestreport import ddt, list_data from common.tool_of_read_config import read_config_yaml from common.tool_of_read_excel import read_xlsx_tool from common.tool_of_log import logger import json import time from setup.put_product import login, img_put config = read_config_yaml("../data_of_config/config.yaml") host = config["host"] cases = read_xlsx_tool(filename=config["case_data_workbook"]["workbook"], sheetname="Sheet2") # cases = read_xlsx_tool() @ddt class Test_cases(unittest.TestCase): def setUp(self) -> None: self.token = login()[\'access_token\'] self.img_id = img_put(self.token, "../setup/000345.png") print(self.token, self.img_id) self.time = time.time() @list_data(cases[0:1]) # 这里必须传入一个列表,所以定位单个用例,不能直接用索引,要用切片[],切出一个只有一个元素的列表 def test_(self, case): data = "" header = "Authorization": f"bearerself.token" # 法1:这里先使用replace替换,注意,相同的,一行代码全部会替换掉 # data = case["入参"].replace("#time#", f"time.time()") # data = case["入参"].replace("#img_id#", self.img_id) # 法2:使用正则表达式替换,搜索出用#——#标记的字段,切掉#后,用反射读取类属性,然后使用replace方法替换 flag = re.finditer("#(.+?)#", case["入参"]) for i in flag: old = i.group() old_ = old.strip("#") new = getattr(Test_cases, old_)#使用反射把属性反写回去 data = case["入参"].replace(old_, new) # 注意这里的“json=“参数,表格读出来是字符串,要转成字典才能做请求入参 data = json.loads(data) # 这里的url是拼接的 response = requests.request(method=case["method"], url=host + case["url"], json=data, headers=header) # response = product_put(self.token,self.img_id) # 这里使用try尝试取结果,返回类型不同使用不同的方法 try: act_res = response.json() except Exception as e: res = response.text act_res = "msg": res print(act_res) expect = json.loads(case["预期结果"]) # try进行断言是为了写日志,再手动抛出异常 for k, v in expect.items(): try: self.assertEqual(v, act_res[k]) except AssertionError as e: logger.error(f\'e\') raise e # self.assertIn(case["预期结果"], res) # print(res) # expect_res = json.loads(case["预期结果"]) # 转成字典 # try: # # self.assertEqual(expect_res, res) # except AssertionError as e: # # logger.error(f"用例---case[\'用例名\']--->执行失败") # logger.error(f"用例---case[\'用例名\']--->执行失败 ====>e") # raise e
test_report
测试报告
练习搭建接口自动化测试框架详细过程
练习搭建接口自动化测试框架(文中所用接口信息、框架思路来自光荣之路培训学校,有意者请搜索光荣之路公众号咨询详细内容)
在设计接口测试框架前,先来弄清楚接口测试怎么进行的,请求和响应都是什么样的,清楚这些之后再进行下一步的操作。
步骤1:新建工程interfaceFramework_practice1,在工程下新建包testScripts用于存放测试脚本文件,在该包下新建testScript.py用户写请求代码
按照接口文档的描述,下面的接口实现了用户的注册、登录、写博客、修改、删除博客等功能,先把每一个接口用代码实现一下。
接口说明:
接口返回code说明:
‘00‘ : 成功
‘01‘:用户已存在
‘02‘:参数不合法
‘03‘:参数错误(1、用户信息错误 2、参数错误,数据库中不存在相应数据)
‘999‘:未知错误,看后台日志
1、用户注册
参数规则说明:
username:
1、必须字母和数字组成
2、长度2~20位
3、字母不区分大小写
password:
1、长度8~20位
2、必须含有字母和数字
email:
标准的email规则
请求的url: http://39.106.41.29:8080/register/
Json串格式参数,请求示例:
{"username": "xufengchai6", "password": "xufengchai121", "email": "[email protected]"}
响应示例:
{"code": "00", "userid": 48}
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
num=random.randint(0,100)
print "register------"
d={"username":"xufengchai%s"%num,"password":"xufengchai121","email":"[email protected]"}
print "data before json.dumps:",d
data = json.dumps(d) #
print "data after json.dumps:",data
r = requests.post(‘http://39.106.41.29:8080/register/‘, data= data)
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
register------
data before json.dumps: {‘username‘: ‘xufengchai6‘, ‘password‘: ‘xufengchai121‘, ‘email‘: ‘[email protected]‘}
data after json.dumps: {"username": "xufengchai6", "password": "xufengchai121", "email": "[email protected]"}
r.status code: 200
r.text: {"code": "00", "userid": 48}
type(r.json()): <type ‘dict‘>
str(r.json()): {u‘code‘: u‘00‘, u‘userid‘: 48}
Process finished with exit code 0
可以看到,请求参数包括用户名,密码,邮箱,返回的是code和userid,code指明注册情况,userid用于后续处理博文的用户标识
2、用户登录
Json串格式参数,请求示例:
{"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
响应示例:
成功:
{"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-05 10:22:05"}
失败:
{"code": "03", "params": {"username": "xufengchai6", "passwod": "7f73a2e4d8b01b0f0f1062a59d4df635"}}
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
md5=hashlib.md5()
md5.update(‘xufengchai121‘)
pwd=md5.hexdigest()#转成16进制
print pwd
print "login------"
data=json.dumps({‘username‘:‘xufengchai6‘,‘password‘:pwd})
print "data:",data
r=requests.post(‘http://39.106.41.29:8080/login/‘,data=data)
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
7f73a2e4d8b01b0f0f1062a59d4df635
login------
data: {"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
r.status code: 200
r.text: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-05 10:22:05"}
type(r.json()): <type ‘dict‘>
str(r.json()): {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-05 10:22:05‘}
Process finished with exit code 0
如果登录成功,会返回token,code,userid等信息
3、新增博文
Json串格式参数,请求示例:
{‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,‘title‘:‘practice‘,‘content‘:‘interface learn‘}
响应示例:
{"data": [{"content": "interface learn", "title": "practice"}], "code": "00", "userid": 48}
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
md5=hashlib.md5()
md5.update(‘xufengchai121‘)
pwd=md5.hexdigest()#转成16进制
print pwd
print "create post------"
data=json.dumps({‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,‘title‘:‘practice‘,‘content‘:‘interface learn‘})
print "data:",data
r=requests.post(‘http://39.106.41.29:8080/create/‘,data=data)
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
7f73a2e4d8b01b0f0f1062a59d4df635
create post------
data: {"content": "interface learn", "token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48, "title": "practice"}
r.status code: 200
r.text: {"data": [{"content": "interface learn", "title": "practice"}], "code": "00", "userid": 48}
type(r.json()): <type ‘dict‘>
str(r.json()): {u‘userid‘: 48, u‘code‘: u‘00‘, u‘data‘: [{u‘content‘: u‘interface learn‘, u‘title‘: u‘practice‘}]}
Process finished with exit code 0
4、查询用户的博文
获取指定用户的博文(支持偏移量)
Offset与lines结合使用,表示跳过offset条取lines条数据,当不传offset或lines时,获取用户全部博文。
Json串格式参数,请求示例:
{"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
响应示例:
{"data": [{"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}], "code": "00", "userid": 48}
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
md5=hashlib.md5()
md5.update(‘xufengchai121‘)
pwd=md5.hexdigest()#转成16进制
print pwd
print "query post of user------"
data=json.dumps({‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘})
print "data:",data
r=requests.post(‘http://39.106.41.29:8080/getBlogsOfUser/‘,data=data)
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
7f73a2e4d8b01b0f0f1062a59d4df635
query post of user------
data: {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
r.status code: 200
r.text: {"data": [{"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}], "code": "00", "userid": 48}
type(r.json()): <type ‘dict‘>
str(r.json()): {u‘userid‘: 48, u‘code‘: u‘00‘, u‘data‘: [{u‘update_time‘: None, u‘title‘: u‘practice‘, u‘content‘: u‘interface learn‘, u‘articleId‘: 2, u‘owner‘: 48, u‘posted_on‘: u‘2018-08-05 10:58:58‘}, {u‘update_time‘: None, u‘title‘: u‘practice‘, u‘content‘: u‘interface learn‘, u‘articleId‘: 1, u‘owner‘: 48, u‘posted_on‘: u‘2018-08-05 10:35:33‘}]}
Process finished with exit code 0
返回的多个博文内容是以一个列表中的多个字典形式存在。
5、修改博文
Json串格式参数,请求示例:
{‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,"articleId":1,"title": "xiaxiaoxu test", "content": "interface learn xia"}
响应示例:
{"articleId": 1, "update_time": "2018-08-05 11:16:05", "code": "00", "userid": 48}
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
md5=hashlib.md5()
md5.update(‘xufengchai121‘)
pwd=md5.hexdigest()#转成16进制
print pwd
print "update post------"
data=json.dumps({‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,"articleId":1,"title": "xiaxiaoxu test", "content": "interface learn xia"})
print "data:",data
r=requests.put(‘http://39.106.41.29:8080/update/‘,data=data)
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
7f73a2e4d8b01b0f0f1062a59d4df635
update post------
data: {"content": "interface learn xia", "articleId": 1, "token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48, "title": "xiaxiaoxu test"}
r.status code: 200
r.text: {"articleId": 1, "update_time": "2018-08-05 11:16:05", "code": "00", "userid": 48}
type(r.json()): <type ‘dict‘>
str(r.json()): {u‘userid‘: 48, u‘update_time‘: u‘2018-08-05 11:16:05‘, u‘code‘: u‘00‘, u‘articleId‘: 1}
Process finished with exit code 0
更新博文后,会返回update_time,表示更新时间
6、查询博文内容
请求地址示例:
http://localhost:8080/getBlogContent/
请求示例:
‘http://39.106.41.29:8080/getBlogContent/‘+str(articleId)
响应示例:
{"code": "00", "data": [{"update_time": "2018-08-05 11:16:05", "title": "xiaxiaoxu test", "content": "interface learn xia", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}]}
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
print "query post------"
articleId=1
r=requests.get(‘http://39.106.41.29:8080/getBlogContent/‘+str(articleId))
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
query post------
r.status code: 200
r.text: {"code": "00", "data": [{"update_time": "2018-08-05 11:16:05", "title": "xiaxiaoxu test", "content": "interface learn xia", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}]}
type(r.json()): <type ‘dict‘>
str(r.json()): {u‘code‘: u‘00‘, u‘data‘: [{u‘update_time‘: u‘2018-08-05 11:16:05‘, u‘title‘: u‘xiaxiaoxu test‘, u‘content‘: u‘interface learn xia‘, u‘articleId‘: 1, u‘owner‘: 48, u‘posted_on‘: u‘2018-08-05 10:35:33‘}]}
Process finished with exit code 0
7、批量查询博文
请求地址示例:
http://localhost:8080/getBlogsContent/articleIds=1,2,3
响应示例:
{"code": "00", "data": [{"update_time": "2018-08-05 11:16:05", "title": "xiaxiaoxu test", "content": "interface learn xia", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}]}
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
print "query posts by blogId------"
r=requests.get(‘http://39.106.41.29:8080/getBlogsContent/‘+str(‘articleIds=1,2‘))
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
query posts by blogId------
r.status code: 200
r.text: {"code": "00", "data": [{"update_time": "2018-08-05 11:16:05", "title": "xiaxiaoxu test", "content": "interface learn xia", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}]}
type(r.json()): <type ‘dict‘>
str(r.json()): {u‘code‘: u‘00‘, u‘data‘: [{u‘update_time‘: u‘2018-08-05 11:16:05‘, u‘title‘: u‘xiaxiaoxu test‘, u‘content‘: u‘interface learn xia‘, u‘articleId‘: 1, u‘owner‘: 48, u‘posted_on‘: u‘2018-08-05 10:35:33‘}, {u‘update_time‘: None, u‘title‘: u‘practice‘, u‘content‘: u‘interface learn‘, u‘articleId‘: 2, u‘owner‘: 48, u‘posted_on‘: u‘2018-08-05 10:58:58‘}]}
Process finished with exit code 0
8、删除博文
Json串格式参数,示例:
{"userid":48, "token": "7f73a2e4d8b01b0f0f1062a59d4df635", "articleId":[2,3,4]}
响应示例:
{"articleId": [2, 3, 4], "code": "00", "userid": 48}
删除之前先查下该用户下有几条博文,6条,articleId从1到6
代码:
#encoding=utf-8
import requests
import json
import os
import hashlib
import random
md5=hashlib.md5()
md5.update(‘xufengchai121‘)
pwd=md5.hexdigest()#转成16进制
print pwd
print "delete post------"
data=json.dumps({‘userid‘:48,‘token‘: 7f73a2e4d8b01b0f0f1062a59d4df635,"articleId":[2,3,4]})
print "data:",data
r=requests.delete(‘http://39.106.41.29:8080/delete/‘,data=data)
print "r.status code:",r.status_code
print "r.text:",r.text
print "type(r.json()):",type(r.json())
print "str(r.json()):",str(r.json())
结果:ok
服务器决绝处理删除请求,可能是服务端针对删除请求做了设置
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
7f73a2e4d8b01b0f0f1062a59d4df635
delete post------
data: {"articleId": [2, 3, 4], "token": "7f73a2e4d8b01b0f0f1062a59d4df635", "userid": 48}
r.status code: 405
r.text: None
Process finished with exit code 0
请求和响应参数:
注册请求和响应参数:
请求示例:
{"username": "xufengchai6", "password": "xufengchai121", "email": "[email protected]"}
响应示例:
{"code": "00", "userid": 48}
登录的请求和响应参数:
请求示例:
{"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
响应示例:
{"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-05 10:22:05"}
查询的请求和响应参数:
请求示例:
{"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
响应示例:
{"data": [{"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}], "code": "00", "userid": 48}
请求的方式:
注册:
d={"username":"xufengchai%s"%num,"password":"xufengchai121","email":"[email protected]"}
data = json.dumps(d)
r = requests.post(‘http://39.106.41.29:8080/register/‘, data= data)
登录:
data=json.dumps({‘username‘:‘xufengchai6‘,‘password‘:pwd})
r=requests.post(‘http://39.106.41.29:8080/login/‘,data=data)
新增博文:
data=json.dumps({‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,‘title‘:‘practice‘,‘content‘:‘interface learn‘})
r=requests.post(‘http://39.106.41.29:8080/create/‘,data=data)
查询:
data=json.dumps({‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘})
r=requests.post(‘http://39.106.41.29:8080/getBlogsOfUser/‘,data=data)
修改:
data=json.dumps({‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,"articleId":1,"title": "xiaxiaoxu test", "content": "interface learn xia"})
r=requests.put(‘http://39.106.41.29:8080/update/‘,data=data)
查询博文内容:
articleId=1
r=requests.get(‘http://39.106.41.29:8080/getBlogContent/‘+str(articleId))
批量查询博文内容:
r=requests.get(‘http://39.106.41.29:8080/getBlogsContent/‘+str(‘articleIds=1,2‘))
删除博文:
data=json.dumps({‘userid‘:48,‘token‘: 7f73a2e4d8b01b0f0f1062a59d4df635,"articleId":[2,3,4]})
r=requests.delete(‘http://39.106.41.29:8080/delete/‘,data=data)
基于以上对接口的请求和响应的归类,我们封装一下http请求的方法
步骤2:封装http请求方法
在工程下新建util包,用于放工具类,在该包下新建HttpClient.py,封装http请求
HttpClient.py
#encoding=utf-8
import requests
import json
class HttpClient(object):
def __init__(self):
pass
def __post(self,url,data=None,json=None,**kargs):
response=requests.post(url=url,data=data,json=json)
return response
def __get(self,url,params=None,**kargs):
response=requests.get(url=url,params=params)
def request(self,requestMethod,requestUrl,paramsType,requestData=None,headers=None,cookies=None):
if requestMethod.lower() == "post":
if paramsType == "form":
response=self.__post(url=requestUrl,data=json.dumps(eval(requestData)),headers=headers,cookies=cookies)
return response
elif paramsType == ‘json‘:
response = self.__post(url=requestUrl,json=json.dumps(eval(requestData)),headers=headers,cookies=cookies)
return response
elif requestMethod == "get":
if paramsType == "url":
request_url="%s%s" %(requestUrl,requestData)
response=self.__get(url=request_url,headers=headers,cookies=cookies)
return response
elif paramsType == "params":
response=self.__get(url=requestUrl,params=requestData,headers=headers,cookies=cookies)
return response
if __name__ == "__main__":
hc=HttpClient()
response=hc.request("post","http://39.106.41.29:8080/register/","form",‘{"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}‘)
print response.text
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/util/httpClient.py
{"username": "xufengchai6", "code": "01"}
Process finished with exit code 0
在工程下新建testData目录,存放数据文件
inter_test_data.xlsx
这个数据文件可以说是本次接口框架的核心,文件中从每个测试用例sheet都代表一个接口,用例中设计了请求数据的格式、请求依赖数据的格式、检查点、是否需要执行、执行结果、错误信息、响应数据、响应状态码等。
该文件的结构设计直接决定了框架的逻辑设计,程序首先会遍历API sheet中的每一行,区分出需要执行的接口用例,然后到对应的接口用例sheet中读取请求数据,发送http请求,对响应做断言和存储并写入结果,之后再进行下一个接口的处理;其中在处理当前接口用例时会有依赖数据(依赖于上一个接口的请求或响应)的处理,文件中会设置所需依赖数据的格式(模板)和请求数据的格式,程序根据请求数据、依赖数据列存储的格式获取到实际用于请求的数据,然后进行发送请求和后续的处理,如何做依赖数据的处理和存储数据的处理是该框架的难点。
下面就一点一点写主程序,看看需要做哪些处理,首先要读取excel文件,需要excel文件的操作,那就把之前封装的excel操作直接拿过来。
步骤3:封装excel操作方法,读取excel数据文件
在uti包下新建parseExcel.py
#encoding=utf-8
import openpyxl
from openpyxl.styles import Border,Side,Font
import time
class ParseExcel(object):
def __init__(self):
self.workbook=None
self.excelFile=None
self.font=Font(color=None)
self.colorDict={‘red‘:‘FFFF3030‘,‘green‘:‘FF008B00‘}
def loadWorkBook(self,excelPathAndName):
#将excel文件加载到内存,并获取其workbook对象
try:
self.workbook=openpyxl.load_workbook(excelPathAndName)
except Exception,e:
raise e
self.excelFile=excelPathAndName
return self.workbook
def getSheetByName(self,sheetName):
try:
sheet=self.workbook[sheetName]
return sheet
except Exception,e:
raise e
def getSheetByIndex(self,sheetIndex):
try:
sheetName=self.workbook.sheetnames[sheetIndex]
except Exception,e:
raise e
sheet=self.workbook[sheetName]
return sheet
def getTotalRowsNumber(self,sheet):
return sheet.max_row
def getTotalColsNumber(self,sheet):
return sheet.max_column
def getStartRowNumber(self,sheet):
return sheet.min_row
def getStartColNumber(self,sheet):
return sheet.min_column
def getSingleRow(self,sheet,rowNo):
#获取sheet中某一行,返回的是这一行所有的数据内容组成的tuple
#下标从1开始,sheet.rows[1]表示第一行
try:
return list(sheet.rows)[rowNo-1]
except Exception,e:
raise e
def getSingleColumn(self,sheet,colNo):
#获取sheet中某一列,返回的是这一列所有的数据组成的tuple
#下标从1开始,sheet.columns[1]表示第一列
try:
return list(sheet.columns)[colNo-1]
except Exception,e:
raise e
def getValueInCell(self,sheet,coordinate=None,rowNo=None,colNo=None):
#根据单元格所在的位置索引获取该单元格中的值,下标从1开始
#sheet.cell(row=1,column=1).value,表示excel中第一行第一列的值
if coordinate != None:#coordinate指坐标,如[‘A2‘]
try:
return sheet[coordinate].value
except Exception,e:
raise e
elif coordinate is None and rowNo is not None and colNo is not None:
try:
return sheet.cell(row=rowNo,column=colNo).value
except Exception,e:
raise e
else:
raise Exception("Argument exception! ")
def writeCell(self,sheet,content,coordinate=None,rowNo=None,colNo=None,color=None):
#根据单元格在excel中的行、列号,或坐标值向单元格中写入数据
#color标识字体的颜色的名字,如red,green
if coordinate:
try:
sheet[coordinate].value=content
if color:
sheet[coordinate].font=Font(color=self.colorDict[color])
self.workbook.save(self.excelFile)
except Exception,e:
raise e
elif coordinate == None and rowNo is not None and colNo is not None:
try:
sheet.cell(row=rowNo,column=colNo).value = content
if color:
sheet.cell(row=rowNo,column=color).font=Font(color=self.colorDict[color])
self.workbook.save(self.excelFile)
except Exception,e:
raise e
else:
raise Exception("Argument exception!")
def writeCurrentTimeInCell(self,sheet,coordinate=None,rowNo=None,colNo=None):
#写入当前时间,下标从1开始,如[‘A1‘]
timeArray=time.localtime(time.time())
currentTime=time.strftime("%Y-%m-%d %H:%M:%S",timeArray)
if coordinate is not None:
try:
sheet[coordinate].value=currentTime
self.workbook.save(self.excelFile)
except Exception,e:
raise e
elif coordinate == None and rowNo is not None and colNo is not None:
try:
sheet.cell(row == rowNo,column=colNo).value=currentTime
self.workbook.save(self.excelFile)
except Exception,e:
raise e
else:
raise Exception("Argument exception!")
if __name__==‘__main__‘:
pe=ParseExcel()
pe.loadWorkBook(r‘d:\\testdata.xlsx‘)
sheetObj=pe.getSheetByName(u"API")
print u"用index号获取sheet对象的名字:",pe.getSheetByIndex(0)
sheet=pe.getSheetByIndex(1)
print type(sheet)
print pe.getTotalRowsNumber(sheetObj)
print pe.getTotalColsNumber(sheetObj)
print pe.getTotalRowsNumber(sheet)
print pe.getTotalColsNumber(sheet)
#print list(sheet.rows)[1]
rows=pe.getSingleRow(sheet,1)
# for i in rows:
# if i.value:
# print i.value
print pe.getValueInCell(sheetObj,‘A1‘)
pe.writeCell(sheet,"xia","A2")
print pe.getValueInCell(sheetObj,"A2")
pe.writeCurrentTimeInCell(sheetObj,"A3")
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/util/ParseExcel.py
用index号获取sheet对象的名字: <Worksheet "API">
<class ‘openpyxl.worksheet.worksheet.Worksheet‘>
13
13
3
1
a
xia
Process finished with exit code 0
在工程下新建config包,在该包下新建config.py文件用户存储公有变量
config.py:
#encoding=utf-8
import os
#项目的绝对路径
projectDir=os.path.dirname(os.path.dirname(__file))
#数据文件的绝对路径
file_path=projectDir + "\\testData\\inter_test_data.xlsx"
在testScript.py文件中读取excel的数据
先把API sheet中的信息读出来:
#encoding=utf-8
import requests
import json
from util.ParseExcel import *
from config.config import *
def testInterface():
pe=ParseExcel()
pe.loadWorkBook(file_path)
sheetObj=pe.getSheetByName(u"API")
executeList=pe.getSingleColumn(sheetObj,7)
#print executeList
for idx,cell in enumerate(executeList[1:],2):#第一行标题去掉,idx从2开始
if cell.value == ‘y‘:
#获取需要执行的接口所在的行对象
rowObj=pe.getSingleRow(sheetObj,idx)
apiName=rowObj[1].value
requestUrl=rowObj[2].value
requestMethod=rowObj[3].value
paramsType=rowObj[4].value
apiCaseSheetName=rowObj[5].value
print apiName,requestUrl,requestMethod,paramsType,apiCaseSheetName
if __name__ == "__main__":
testInterface()
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
register http://39.106.41.29:8080/register/ post form 注册接口用例
login http://39.106.41.29:8080/login/ post form 登录接口用例
Process finished with exit code 0
可以看到在读取需要执行的行中每一列的内容时,用的索引号都是数字,这个不用说,肯定是要在公共变量中存一下的。
在config文件中把每个sheet中的列号用变量存起来
#API sheet中所需列号
API_apiName=2
API_requestUrl=3
API_requestMethod=4
API_paramsType=5
API_apiCaseSheetName=6
API_ifExecute=7
#接口用例sheet中所需列号
CASE_requestData=1
CASE_relyData=2
CASE_responseCode=3
CASE_responseData=4
CASE_dataStore=5
CASE_checkPoint=6
CASE_ifExecute=7
CASE_result=8
CASE_errorInfo=9
接着把caseSheet中的数据读出来
#下一步读取用例sheet表,准备执行测试用例
caseSheetObj=pe.getSheetByName(apiCaseSheetName)#获取用例sheet对象
caseExecuteCol=pe.getSingleColumn(caseSheetObj,CASE_ifExecute)#获取用例sheet对象中需要执行的列
#遍历需要执行的列
for c_idx,c_cell in enumerate(caseExecuteCol[1:],2):#标题行忽略,c_idx从2开始
if c_cell.value == ‘y‘:
#说明case行需要执行
caseRowObj=pe.getSingleRow(caseSheetObj,c_idx)
requestData=caseRowObj[CASE_requestData-1].value
relyData=caseRowObj[CASE_relyData-1].value
dataStore=caseRowObj[CASE_dataStore-1].value
checkPoint=caseRowObj[CASE_checkPoint-1].value
#print requestData,relyData,dataStore,checkPoint
#if relyData:#relyData列有值的话,需要获取依赖数据
#发送接口数据之前,先做依赖数据的处理
写到这儿让我们来分析一下这个框架的结构和逻辑,它是如何对每个接口进行测试和断言的?这个问题想明白了,这个框架的核心就确定了。
先看下文档中接口的信息:
在注册接口中,请求数据RequestData直接给出了,那么把这列数据取出来,直接发送请求就可以了,然后把结果写在responseData列,注册接口响应参数有code和userid, userid是一个主键,唯一标识一个用户,在checkPoint列设置检查点,和响应code做对比,如果匹配00,就说明注册接口请求成功了,把Result列写入pass。
因为请求数据直接给出了,不需要依赖别的数据,那RelyData列置空,然后有个ResponseCode列需要写一下请求的状态码,比如返回200,就写200,然后看DataStore列,这一列是用来存储接口请求的信息的,可以写请求的参数信息,也可以写响应的参数信息,这个存储数据是干啥的呢,它是用来为其他接口请求做准备数据的;
比如登录接口,在登录之前,需要获取用户的用户名和密码,那它就需要从注册接口的信息来获取,可以在注册接口的DataStore列把用户名和密码信息直接写进去,如username:xiaxiaoxu,password:ksdfjlks,然后再运行登录接口请求时,过来把数据取出来,直接发送请求就行了,登录接口返回的参数有token和userid,这个token是查询博文接口用的;
接下来我们再把各个接口的请求参数和响应参数列出来,然后来看下下游接口的请求数据是如何依赖上游接口的数据的。
注册接口:
请求:
{"username": "xufengchai6", "password": "xufengchai121", "email": "[email protected]"}
响应:
{"code": "00", "userid": 48}
---------------------------------------------------------------------------------------------------------------------------
登录接口:
请求:
{"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
这里的username、password依赖于注册接口中的请求数据
响应:
{"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-05 10:22:05"}
---------------------------------------------------------------------------------------------------------------------------
新增博文:
请求:
{‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,‘title‘:‘practice‘,‘content‘:‘interface learn‘}
这里的userid、token依赖于登录接口中的响应数据
响应:
{"data": [{"content": "interface learn", "title": "practice"}], "code": "00", "userid": 48}
---------------------------------------------------------------------------------------------------------------------------
查询用户博文:
请求:
{"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
这里的token、userid可以依赖于新增博文接口的响应数据,也可以依赖登录于接口的响应数据
响应:
{"data": [{"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}], "code": "00", "userid": 48}
---------------------------------------------------------------------------------------------------------------------------
修改博文:
请求:
{‘userid‘:48,‘token‘:‘290cbf8f1438f3d8f56d9026604de336‘,"articleId":1,"title": "xiaxiaoxu test", "content": "interface learn xia"}
这里的articleId、title中的可以依赖于查询用户博文接口的响应数据,userid、token可以依赖于查询用户博文接口的请求数据
响应:
{"articleId": 1, "update_time": "2018-08-05 11:16:05", "code": "00", "userid": 48}
---------------------------------------------------------------------------------------------------------------------------
查询博文内容:
请求:
‘http://39.106.41.29:8080/getBlogContent/‘+str(articleId)
这里的articleId可以依赖于查询用户博文接口的响应数据,也可以依赖于修改博文的请求数据,但是修改博文接口不一定会运行,依赖于查询用户博文接口会稳妥一些
响应:
{"code": "00", "data": [{"update_time": "2018-08-05 11:16:05", "title": "xiaxiaoxu test", "content": "interface learn xia", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}]}
---------------------------------------------------------------------------------------------------------------------------
批量查询博文内容:
请求:
http://localhost:8080/getBlogsContent/articleIds=1,2,3
这里的articleIds可以依赖于查询用户博文接口的响应数据
响应:
{"code": "00", "data": [{"update_time": "2018-08-05 11:16:05", "title": "xiaxiaoxu test", "content": "interface learn xia", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}]}
通过上边各个接口的数据依赖关系,可以看出某个接口在请求的之前,要先根据依赖关系到某个上游接口的请求或者响应数据中获取需要的数据,那么怎么去把这个数据获取到呢?以什么规则去获取呢?把这两个个问题解决了,我认为这个框架就成功了一半了!
为啥呢,因为我一半的时间都用来思考这两个问题了,虽然事先已经知道了答案,但是为了透彻的理解其中的原理,花了很长时间来推理这个答案的合理性和必然性~~
对于这种框架的原理,我总是尝试着把所有的细节都揉碎了来重新梳理,推理出每一个步骤的合理性,这样会理解的更深一些。
初步的思路是每个接口在运行的时候把请求数据和响应数据都写到文件里,然后下一个接口在访问的时候去依赖接口中读取请求数据或者响应数据,然后再把请求和响应数据写到自己的sheet表中,以此类推,接着考虑一下实现的细节,这个框架要兼顾多个接口的运行,每个接口的请求数据、存储数据的方法需要用封装的函数来进行处理,这就需要把这些接口的数据信息的获取抽象出一个公用的方法,这个方法可以实现所有接口的数据处理,比如某个接口在获取请求数据时,要先根据接口的请求参数设置一个依赖规则,指明每个参数需要到哪个接口的哪个用例的请求数据或者响应数据中,读取哪个参数的值,每个参数可能需要到不同的接口中获取数据,这个依赖规则需要给出具体的接口名、用例号(行号)、数据类型是请求的还是响应的,然后根据数据类型到不同的列号取值,取的时候,先找到单元格,把内容读出来,转成字典的格式,然后找所需的键值对,有的接口字典深度不止一层(如查询接口),那就要再做一层处理,获取完请求数据信息,就可以发送请求了,之后再把响应数据写到文件中,后续接口可能会用到。
步骤4:封装方法获取请求数据
在action包下新建文件getRequestData.py:
getRequestData.py
# encoding=utf-8
from util.ParseExcel import *
import os
from config.config import *
import json
class GetRequestData(object):
def __init__(self):
pass
@classmethod
def getRequestData(cls, relyRule):
pe = ParseExcel()
pe.loadWorkBook(file_path)
requestData = {}
# relySource = {"token": "", "userid": "","articleid":""}
# relyRule = {"token":"userRegister->1->request","userid":"register->1->response","articleid":"userLogin->1->response->[‘data‘][0]"}
interDict = {"userRegister": "register",
"userLogin": "login",
"searchUserBlog": "search",
"modifyBlog": "modify"}
dataTypeColDict = {"request": CASE_requestData,
"response": CASE_responseData}
# 根据接口名映射一个字典,对应接口sheet名称
# 获取到依赖接口的sheet页,case号(行号),请求列或者响应列号,
# 然后到对应单元格取数据,转成字典格式,
# 取出里面对应层级的数据
for key, value in relyRule.items(): # "articleid":"userLogin->1->response->[‘data‘][0]"
print "key:value==>", key, ":", value
rList = value.split("->")
print "len(rList):", len(rList)
if len(rList) == 4:
# 说明所需数据在依赖数据的第二层
interfaceType, caseNo, dataType, dataKey = rList
# print "interfaceType,caseNo,dataType,dataKey:",interfaceType,‘,‘,caseNo,‘,‘,dataType,‘,‘,dataKey
# print "interDict[interfaceType]:",interDict[interfaceType]
# print "dataTypeColDict[dataType]:",dataTypeColDict[dataType]
interSheetObj = pe.getSheetByName(interDict[interfaceType])
# print "interSheetObj:",interSheetObj
relyContent = json.loads(
pe.getValueInCell(interSheetObj, rowNo=int(caseNo) + 1, colNo=dataTypeColDict[dataType]))
# print "relyContent:",relyContent
# print "type(relyContent):",type(relyContent)
command = "%s%s[‘%s‘]" % (relyContent, dataKey, key)
# print "command:",command
requestData[key] = eval(command)
print "requestData-3:",requestData
elif len(rList) == 3:
# 说明所需数据在依赖数据的第一层
# "token":"userLogin->1->response"
interfaceType, caseNo, dataType = rList
# print "interfaceType,caseNo,dataType:",interfaceType,‘,‘,caseNo,‘,‘,dataType
# print "interDict[interfaceType]:",interDict[interfaceType]
# print "dataTypeColDict[dataType]:",dataTypeColDict[dataType]
interSheetObj = pe.getSheetByName(interDict[interfaceType])
# print "interSheetObj:",interSheetObj
contentStr = ‘%s‘ % pe.getValueInCell(interSheetObj, rowNo=int(caseNo) + 1,
colNo=dataTypeColDict[dataType])
print "contentStr:", contentStr
print "type(contentStr):", type(contentStr)
relyContent = json.loads(contentStr)
print "relyContent:", relyContent
# print "type(relyContent):",type(relyContent)
command = "%s[‘%s‘]" % (relyContent, key)
# print "command:",command
requestData[key] = eval(command)
print "requestData-1:",requestData
else:
requestData[key] = value
return requestData
if __name__ == "__main__":
relyRule = {"articleId": "searchUserBlog->1->response->[‘data‘][0]", "token": "userLogin->1->response",
"content": "xiaxiaoxu"}
print GetRequestData.getRequestData(relyRule)
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/action/getRequestData.py
key:value==> content : xiaxiaoxu
len(rList): 1
key:value==> token : userLogin->1->response
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-05 10:22:05"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-05 10:22:05‘}
requestData-1: {‘content‘: ‘xiaxiaoxu‘, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘}
key:value==> articleId : searchUserBlog->1->response->[‘data‘][0]
len(rList): 4
requestData-3: {‘content‘: ‘xiaxiaoxu‘, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘articleId‘: 2}
{‘content‘: ‘xiaxiaoxu‘, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘articleId‘: 2}
Process finished with exit code 0
这种获取请求数据方法的思路是,在每个接口中把请求的数据设置一个依赖的规则,用来指明所需的参数来自哪个接口的请求数据还是响应数据,然后程序根据这个依赖规则,去所依赖的地方获取数据。
例如
relyRule={"articleId":"searchUserBlog->1->response->[‘data‘][0]","token":"userLogin->1->response","content": "xiaxiaoxu"},
这个依赖规则是个json串,用json编辑器可以看到它的结构:
可以看到它有三个兄弟节点,代表该接口需要三个请求参数,每个兄弟节点下边是这个参数所依赖的规则,比如"articleId":"searchUserBlog->1->response->[‘data‘][0]",表示"articleId"这个参数依赖的数据是searchUserBlog接口的数据,具体是在searchUserBlog接口的第一个用例的响应数据中的[‘data’]对应的数据中的第1个articleId字段的值,其所依赖的接口数据存储的格式也是json串的形式,可以转成python的字典结构,这样依赖规则的每一层数据都可以用作为字典的key取出对应的value值,这个value值又作为新一层字典来取值。
下面是searchUserBlog接口的响应数据的例子:
{"data": [{"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}], "code": "00", "userid": 48}
需要注意的是,在每个接口获取请求数据时,它所依赖的接口的数据要事先存在,否则就取不到数据了,所以,在处理完每个接口的请求后,需要把请求数据和响应数据写入文件,以备后续接口使用。
下一步实现写入文件的方法
步骤5:实现写入文件的方法
在action包下新建write_test_result.py
write_test_result.py
#encoding=utf-8
from config import *
from util.ParseExcel import *
from action.getRequestData import *
import json
def write_result(wbObj,sheetObj,rowNum,requestData=None,responseData=None,errorKey = None):
try:
if requestData:
#写入请求数据
print "requestData-4:",requestData
print "type(requestData-4):",type(requestData)
wbObj.writeCell(sheet=sheetObj,content=requestData,rowNo=rowNum,colNo=CASE_requestData)
if responseData:
#写响应body
wbObj.writeCell(sheet=sheetObj,content=responseData,rowNo=rowNum,colNo=CASE_responseData)
#写校验结果状态列及错误信息列
if errorKey:
wbObj.writeCell(sheet=sheetObj,content="failed",rowNo=rowNum,colNo=CASE_result)
wbObj.writeCell(sheet=sheetObj,content="%s" %errorKey,rowNo = rowNum,colNo=CASE_errorInfo)
else:
wbObj.writeCell(sheetObj,content="pass",rowNo=rowNum,colNo=CASE_result)
except Exception,e:
raise e
在主程序中调用该方法:
testScript.py:
#encoding=utf-8
import requests
import json
from util.ParseExcel import *
from util.httpClient import *
from config.config import *
from action.write_test_result import *
def testInterface():
parseE = ParseExcel()
parseE.loadWorkBook(file_path)
sheetObj = parseE.getSheetByName(u"API")
activeList = parseE.getSingleColumn(sheetObj, API_ifExecute)
for idx, cell in enumerate(activeList[1:], 2):
if cell.value == ‘y‘:
rowObj = parseE.getSingleRow(sheetObj, idx)
apiName = rowObj[API_apiName - 1].value
requestUrl = rowObj[API_requestUrl - 1].value
requestMethod = rowObj[API_requestMethod - 1].value
paramsType = rowObj[API_paramsType - 1].value
apiTestCaseFileName = rowObj[API_apiCaseSheetName - 1].value
# print apiName,requestUrl,requestMethod,paramsType,apiTestCaseFileName
# 下一步读sheet表,准备执行测试用例
print "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
print "apiTestCaseFileName:", apiTestCaseFileName
caseSheetObj = parseE.getSheetByName(apiTestCaseFileName)
caseActiveObj = parseE.getSingleColumn(caseSheetObj, CASE_ifExecute)
for c_idx, col in enumerate(caseActiveObj[1:], 2):
if col.value == ‘y‘:
caseRowObj = parseE.getSingleRow(caseSheetObj, c_idx)
relyRule = caseRowObj[CASE_relyRule - 1].value
print "relyRule:", relyRule
if relyRule:
# 发送接口请求之前,先获取请求数据
requestData = json.dumps(GetRequestData.getRequestData(eval(relyRule)))
print "requestData-2:", requestData
write_result(parseE,caseSheetObj,c_idx,requestData=requestData)
# print "print requestData Done!"
if __name__ == "__main__":
testInterface()
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: register
relyRule: None
relyRule: None
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: login
relyRule: {"username":"userRegister->1->request","password":"userRegister->1->request"}
key:value==> username : userRegister->1->request
len(rList): 3
contentStr: {"username":"srsdcx01","password":"wcx123wac1","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘srsdcx01‘, u‘password‘: u‘wcx123wac1‘, u‘email‘: u‘[email protected]‘}
requestData-1: {‘username‘: u‘srsdcx01‘}
key:value==> password : userRegister->1->request
len(rList): 3
contentStr: {"username":"srsdcx01","password":"wcx123wac1","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘srsdcx01‘, u‘password‘: u‘wcx123wac1‘, u‘email‘: u‘[email protected]‘}
requestData-1: {‘username‘: u‘srsdcx01‘, ‘password‘: u‘wcx123wac1‘}
requestData-2: {"username": "srsdcx01", "password": "wcx123wac1"}
requestData-4: {"username": "srsdcx01", "password": "wcx123wac1"}
type(requestData-4): <type ‘str‘>
relyRule: {"username":"userRegister->2->request","password":"userRegister->2->request"}
key:value==> username : userRegister->2->request
len(rList): 3
contentStr: {"username":"wcd23x","password":"wcx123wac","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘wcd23x‘, u‘password‘: u‘wcx123wac‘, u‘email‘: u‘[email protected]‘}
requestData-1: {‘username‘: u‘wcd23x‘}
key:value==> password : userRegister->2->request
len(rList): 3
contentStr: {"username":"wcd23x","password":"wcx123wac","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘wcd23x‘, u‘password‘: u‘wcx123wac‘, u‘email‘: u‘[email protected]‘}
requestData-1: {‘username‘: u‘wcd23x‘, ‘password‘: u‘wcx123wac‘}
requestData-2: {"username": "wcd23x", "password": "wcx123wac"}
requestData-4: {"username": "wcd23x", "password": "wcx123wac"}
type(requestData-4): <type ‘str‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: search
relyRule: {"token":"userLogin->1->response","userid":"userLogin->1->response"}
key:value==> token : userLogin->1->response
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-05 10:22:05"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-05 10:22:05‘}
requestData-1: {‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘}
key:value==> userid : userLogin->1->response
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-05 10:22:05"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-05 10:22:05‘}
requestData-1: {‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘userid‘: 48}
requestData-2: {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
requestData-4: {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
type(requestData-4): <type ‘str‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: modify
relyRule: {"userid":"searchUserBlog->1->response","token":"searchUserBlog->1->request","articleId":"searchUserBlog->1->response->[‘data‘][0]","title":"searchUserBlog->1->response->[‘data‘][0]","content":"interface learn xia"}
key:value==> content : interface learn xia
len(rList): 1
key:value==> articleId : searchUserBlog->1->response->[‘data‘][0]
len(rList): 4
requestData-3: {‘content‘: ‘interface learn xia‘, ‘articleId‘: 2}
key:value==> token : searchUserBlog->1->request
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘userid‘: 48}
requestData-1: {‘content‘: ‘interface learn xia‘, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘articleId‘: 2}
key:value==> userid : searchUserBlog->1->response
len(rList): 3
contentStr: {"data": [{"update_time": null, "title": "practice", "content": "interface learn", "articleId": 2, "owner": 48, "posted_on": "2018-08-05 10:58:58"}, {"update_time": null, "title": "practice", "content": "interface learn", "articleId": 1, "owner": 48, "posted_on": "2018-08-05 10:35:33"}], "code": "00", "userid": 48}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘userid‘: 48, u‘code‘: u‘00‘, u‘data‘: [{u‘update_time‘: None, u‘title‘: u‘practice‘, u‘content‘: u‘interface learn‘, u‘articleId‘: 2, u‘owner‘: 48, u‘posted_on‘: u‘2018-08-05 10:58:58‘}, {u‘update_time‘: None, u‘title‘: u‘practice‘, u‘content‘: u‘interface learn‘, u‘articleId‘: 1, u‘owner‘: 48, u‘posted_on‘: u‘2018-08-05 10:35:33‘}]}
requestData-1: {‘content‘: ‘interface learn xia‘, ‘userid‘: 48, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘articleId‘: 2}
key:value==> title : searchUserBlog->1->response->[‘data‘][0]
len(rList): 4
requestData-3: {‘content‘: ‘interface learn xia‘, ‘userid‘: 48, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘articleId‘: 2, ‘title‘: u‘practice‘}
requestData-2: {"content": "interface learn xia", "userid": 48, "token": "290cbf8f1438f3d8f56d9026604de336", "articleId": 2, "title": "practice"}
requestData-4: {"content": "interface learn xia", "userid": 48, "token": "290cbf8f1438f3d8f56d9026604de336", "articleId": 2, "title": "practice"}
type(requestData-4): <type ‘str‘>
Process finished with exit code 0
文件中的请求数据已经写进去了,先忽略响应数据
发现忘了处理passord的加密过程了,在getRequestdata.py中处理一下
在util包下新建md5_encrypt.py用户加密处理
md5_encrypt.py:
#encoding=utf-8
import hashlib
def md5_encrypt(text):
m5 = hashlib.md5()
m5.update(text)
value = m5.hexdigest()
return value
if __name__ == "__main__":
print md5_encrypt("xiaxiaoxu")
结果:ok
D: est_pythoninterfaceFramework>python md5_encrypt.py
a2f93c01757358aa9c3ee4372a2f4eca
修改getRequestData.py文件加入密码加密处理:
# encoding=utf-8
from util.ParseExcel import *
import os
from config.config import *
import json
from util.md5_encrypt import *
class GetRequestData(object):
def __init__(self):
pass
@classmethod
def getRequestData(cls, relyRule):
pe = ParseExcel()
pe.loadWorkBook(file_path)
requestData = {}
# relySource = {"token": "", "userid": "","articleid":""}
# relyRule = {"token":"userRegister->1->request","userid":"register->1->response","articleid":"userLogin->1->response->[‘data‘][0]"}
interDict = {"userRegister": "register",
"userLogin": "login",
"searchUserBlog": "search",
"modifyBlog": "modify"}
dataTypeColDict = {"request": CASE_requestData,
"response": CASE_responseData}
# 根据接口名映射一个字典,对应接口sheet名称
# 获取到依赖接口的sheet页,case号(行号),请求列或者响应列号,
# 然后到对应单元格取数据,转成字典格式,
# 取出里面对应层级的数据
for key, value in relyRule.items(): # "articleid":"userLogin->1->response->[‘data‘][0]"
print "key:value==>", key, ":", value
rList = value.split("->")
print "len(rList):", len(rList)
if len(rList) == 4:
# 说明所需数据在依赖数据的第二层
interfaceType, caseNo, dataType, dataKey = rList
# print "interfaceType,caseNo,dataType,dataKey:",interfaceType,‘,‘,caseNo,‘,‘,dataType,‘,‘,dataKey
# print "interDict[interfaceType]:",interDict[interfaceType]
# print "dataTypeColDict[dataType]:",dataTypeColDict[dataType]
interSheetObj = pe.getSheetByName(interDict[interfaceType])
# print "interSheetObj:",interSheetObj
relyContent = json.loads(
pe.getValueInCell(interSheetObj, rowNo=int(caseNo) + 1, colNo=dataTypeColDict[dataType]))
# print "relyContent:",relyContent
# print "type(relyContent):",type(relyContent)
command = "%s%s[‘%s‘]" % (relyContent, dataKey, key)
# print "command:",command
requestData[key] = eval(command)
print "requestData-3:",requestData
elif len(rList) == 3:
# 说明所需数据在依赖数据的第一层
# "token":"userLogin->1->response"
interfaceType, caseNo, dataType = rList
# print "interfaceType,caseNo,dataType:",interfaceType,‘,‘,caseNo,‘,‘,dataType
# print "interDict[interfaceType]:",interDict[interfaceType]
# print "dataTypeColDict[dataType]:",dataTypeColDict[dataType]
interSheetObj = pe.getSheetByName(interDict[interfaceType])
# print "interSheetObj:",interSheetObj
contentStr = ‘%s‘ % pe.getValueInCell(interSheetObj, rowNo=int(caseNo) + 1,
colNo=dataTypeColDict[dataType])
print "contentStr:", contentStr
print "type(contentStr):", type(contentStr)
relyContent = json.loads(contentStr)
print "relyContent:", relyContent
# print "type(relyContent):",type(relyContent)
command = "%s[‘%s‘]" % (relyContent, key)
# print "command:",command
print "key:eval(command):",key,eval(command)
requestData[key] = md5_encrypt(eval(command)) if key == "password" else eval(command)
print "requestData-1:",requestData
else:
requestData[key] = value
return requestData
if __name__ == "__main__":
relyRule = {"articleId": "searchUserBlog->1->response->[‘data‘][0]", "password": "userRegister->1->request",
"content": "xiaxiaoxu"}
print GetRequestData.getRequestData(relyRule)
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/action/getRequestData.py
key:value==> content : xiaxiaoxu
len(rList): 1
key:value==> password : userRegister->1->request
len(rList): 3
contentStr: {"username":"srsdcx01","password":"wcx123wac1","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘srsdcx01‘, u‘password‘: u‘wcx123wac1‘, u‘email‘: u‘[email protected]‘}
key:eval(command): password wcx123wac1
requestData-1: {‘content‘: ‘xiaxiaoxu‘, ‘password‘: ‘d85fb67e312ed3a589951720a6f3b079‘}
key:value==> articleId : searchUserBlog->1->response->[‘data‘][0]
len(rList): 4
requestData-3: {‘content‘: ‘xiaxiaoxu‘, ‘password‘: ‘d85fb67e312ed3a589951720a6f3b079‘, ‘articleId‘: 2}
{‘content‘: ‘xiaxiaoxu‘, ‘password‘: ‘d85fb67e312ed3a589951720a6f3b079‘, ‘articleId‘: 2}
Process finished with exit code 0
至此,已经实现了获取请求数据,封装了http请求的方法,那么把请求数据用http请求的方式发送给服务器就完成了接口的请求过程,下面在主程序中加入发送请求和写入结果的处理
步骤 6:处理发送请求和写入结果
在主程序中添加http请求处理,并把请求数据和响应数据写入文件
testScript.py:
#encoding=utf-8
import requests
import json
from util.ParseExcel import *
from util.httpClient import *
from config.config import *
from action.write_test_result import *
from action.check_result import *
def testInterface():
parseE = ParseExcel()
parseE.loadWorkBook(file_path)
sheetObj = parseE.getSheetByName(u"API")
activeList = parseE.getSingleColumn(sheetObj, API_ifExecute)
for idx, cell in enumerate(activeList[1:], 2):
if cell.value == ‘y‘:
rowObj = parseE.getSingleRow(sheetObj, idx)
apiName = rowObj[API_apiName - 1].value
requestUrl = rowObj[API_requestUrl - 1].value
requestMethod = rowObj[API_requestMethod - 1].value
paramsType = rowObj[API_paramsType - 1].value
apiTestCaseFileName = rowObj[API_apiCaseSheetName - 1].value
# print apiName,requestUrl,requestMethod,paramsType,apiTestCaseFileName
# 下一步读sheet表,准备执行测试用例
print "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
print "apiTestCaseFileName:", apiTestCaseFileName
caseSheetObj = parseE.getSheetByName(apiTestCaseFileName)
caseActiveObj = parseE.getSingleColumn(caseSheetObj, CASE_ifExecute)
for c_idx, col in enumerate(caseActiveObj[1:], 2):
if col.value == ‘y‘:
caseRowObj = parseE.getSingleRow(caseSheetObj, c_idx)
relyRule = caseRowObj[CASE_relyRule - 1].value
checkPoint=caseRowObj[CASE_checkPoint-1].value
print "relyRule:", relyRule
if relyRule:
# 发送接口请求之前,先获取请求数据
requestData = json.dumps(GetRequestData.getRequestData(eval(relyRule)))
print "requestData-2:", requestData
else:
requestData=caseRowObj[CASE_requestData-1].value
print "requestData without relyRule:",requestData,type(requestData)
hc = HttpClient()
print "requestMethod, requestUrl, paramsType, requestData:"
print requestMethod, requestUrl, paramsType, requestData
response = hc.request(requestMethod=requestMethod,
requestUrl=requestUrl,
paramsType=paramsType,
requestData=requestData
)
print "#############response.text##############:",response.text
if response.status_code ==200:
responseData=response.text
print "responseData:",responseData
write_result(parseE,caseSheetObj,c_idx,requestData=requestData,responseData=responseData)
结果: ok
D: est_pythoninterfaceFramework>python testScript.py
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: searchUserBlog
relyRule: {"token":"userLogin->1->response","userid":"userLogin->1->response"}
key:value==> token : userLogin->1->response
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-15 12:20:06"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-15 12:20:06‘}
key:eval(command): token 290cbf8f1438f3d8f56d9026604de336
requestData-1: {‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘}
key:value==> userid : userLogin->1->response
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-15 12:20:06"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-15 12:20:06‘}
key:eval(command): userid 48
requestData-1: {‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘userid‘: 48}
requestData-2: {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/getBlogsOfUser/ form {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
#############response.text##############: {"code": "03", "params": {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}}
responseData: {"code": "03", "params": {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}}
requestData-4: {"token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48}
type(requestData-4): <type ‘str‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: addBlog
relyRule: {"userid":"userLogin->1->response","token":"userLogin->1->response","titile":"xiaxiaoxu‘s blog","content":"keep learning"}
key:value==> content : keep learning
len(rList): 1
key:value==> token : userLogin->1->response
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-15 12:20:06"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-15 12:20:06‘}
key:eval(command): token 290cbf8f1438f3d8f56d9026604de336
requestData-1: {‘content‘: ‘keep learning‘, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘}
key:value==> userid : userLogin->1->response
len(rList): 3
contentStr: {"token": "290cbf8f1438f3d8f56d9026604de336", "code": "00", "userid": 48, "login_time": "2018-08-15 12:20:06"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, u‘code‘: u‘00‘, u‘userid‘: 48, u‘login_time‘: u‘2018-08-15 12:20:06‘}
key:eval(command): userid 48
requestData-1: {‘content‘: ‘keep learning‘, ‘token‘: u‘290cbf8f1438f3d8f56d9026604de336‘, ‘userid‘: 48}
key:value==> titile : xiaxiaoxu‘s blog
len(rList): 1
requestData-2: {"content": "keep learning", "token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48, "titile": "xiaxiaoxu‘s blog"}
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/create/ form {"content": "keep learning", "token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48, "titile": "xiaxiaoxu‘s blog"}
#############response.text##############: {"code": "03", "params": {"content": "keep learning", "token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48, "titile": "xiaxiaoxu‘s blog"}}
responseData: {"code": "03", "params": {"content": "keep learning", "token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48, "titile": "xiaxiaoxu‘s blog"}}
requestData-4: {"content": "keep learning", "token": "290cbf8f1438f3d8f56d9026604de336", "userid": 48, "titile": "xiaxiaoxu‘s blog"}
type(requestData-4): <type ‘str‘>
D: est_pythoninterfaceFramework>python testScript.py
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: register
relyRule: None
requestData without relyRule: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"} <type ‘unicode‘>
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/register/ form {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
#############response.text##############: {"code": "00", "userid": 17}
responseData: {"code": "00", "userid": 17}
requestData-4: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
type(requestData-4): <type ‘unicode‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: login
relyRule: {"username":"userRegister->1->request","password":"userRegister->1->request"}
key:value==> username : userRegister->1->request
len(rList): 3
contentStr: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘xufengchai6‘, u‘password‘: u‘xufengchai121‘, u‘email‘: u‘[email protected]‘}
key:eval(command): username xufengchai6
requestData-1: {‘username‘: u‘xufengchai6‘}
key:value==> password : userRegister->1->request
len(rList): 3
contentStr: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘xufengchai6‘, u‘password‘: u‘xufengchai121‘, u‘email‘: u‘[email protected]‘}
key:eval(command): password xufengchai121
requestData-1: {‘username‘: u‘xufengchai6‘, ‘password‘: ‘7f73a2e4d8b01b0f0f1062a59d4df635‘}
requestData-2: {"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/login/ form {"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
#############response.text##############: {"token": "5e546324ba91858b26216d399dee33e1", "code": "00", "userid": 17, "login_time": "2018-08-16 12:17:59"}
responseData: {"token": "5e546324ba91858b26216d399dee33e1", "code": "00", "userid": 17, "login_time": "2018-08-16 12:17:59"}
requestData-4: {"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
type(requestData-4): <type ‘str‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: addBlog
relyRule: {"userid":"userLogin->1->response","token":"userLogin->1->response","titile":"xiaxiaoxu‘s blog","content":"keep learning"}
key:value==> content : keep learning
len(rList): 1
key:value==> token : userLogin->1->response
len(rList): 3
contentStr: {"token": "5e546324ba91858b26216d399dee33e1", "code": "00", "userid": 17, "login_time": "2018-08-16 12:17:59"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘5e546324ba91858b26216d399dee33e1‘, u‘code‘: u‘00‘, u‘userid‘: 17, u‘login_time‘: u‘2018-08-16 12:17:59‘}
key:eval(command): token 5e546324ba91858b26216d399dee33e1
requestData-1: {‘content‘: ‘keep learning‘, ‘token‘: u‘5e546324ba91858b26216d399dee33e1‘}
key:value==> userid : userLogin->1->response
len(rList): 3
contentStr: {"token": "5e546324ba91858b26216d399dee33e1", "code": "00", "userid": 17, "login_time": "2018-08-16 12:17:59"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘5e546324ba91858b26216d399dee33e1‘, u‘code‘: u‘00‘, u‘userid‘: 17, u‘login_time‘: u‘2018-08-16 12:17:59‘}
key:eval(command): userid 17
requestData-1: {‘content‘: ‘keep learning‘, ‘token‘: u‘5e546324ba91858b26216d399dee33e1‘, ‘userid‘: 17}
key:value==> titile : xiaxiaoxu‘s blog
len(rList): 1
requestData-2: {"content": "keep learning", "token": "5e546324ba91858b26216d399dee33e1", "userid": 17, "titile": "xiaxiaoxu‘s blog"}
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/create/ form {"content": "keep learning", "token": "5e546324ba91858b26216d399dee33e1", "userid": 17, "titile": "xiaxiaoxu‘s blog"}
#############response.text##############: {"code": "03", "params": {"content": "keep learning", "token": "5e546324ba91858b26216d399dee33e1", "userid": 17, "titile": "xiaxiaoxu‘s blog"}}
responseData: {"code": "03", "params": {"content": "keep learning", "token": "5e546324ba91858b26216d399dee33e1", "userid": 17, "titile": "xiaxiaoxu‘s blog"}}
requestData-4: {"content": "keep learning", "token": "5e546324ba91858b26216d399dee33e1", "userid": 17, "titile": "xiaxiaoxu‘s blog"}
type(requestData-4): <type ‘str‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: searchUserBlog
relyRule: {"token":"userLogin->1->response","userid":"userLogin->1->response"}
key:value==> token : userLogin->1->response
len(rList): 3
contentStr: {"token": "5e546324ba91858b26216d399dee33e1", "code": "00", "userid": 17, "login_time": "2018-08-16 12:17:59"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘5e546324ba91858b26216d399dee33e1‘, u‘code‘: u‘00‘, u‘userid‘: 17, u‘login_time‘: u‘2018-08-16 12:17:59‘}
key:eval(command): token 5e546324ba91858b26216d399dee33e1
requestData-1: {‘token‘: u‘5e546324ba91858b26216d399dee33e1‘}
key:value==> userid : userLogin->1->response
len(rList): 3
contentStr: {"token": "5e546324ba91858b26216d399dee33e1", "code": "00", "userid": 17, "login_time": "2018-08-16 12:17:59"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘5e546324ba91858b26216d399dee33e1‘, u‘code‘: u‘00‘, u‘userid‘: 17, u‘login_time‘: u‘2018-08-16 12:17:59‘}
key:eval(command): userid 17
requestData-1: {‘token‘: u‘5e546324ba91858b26216d399dee33e1‘, ‘userid‘: 17}
requestData-2: {"token": "5e546324ba91858b26216d399dee33e1", "userid": 17}
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/getBlogsOfUser/ form {"token": "5e546324ba91858b26216d399dee33e1", "userid": 17}
#############response.text##############: {"data": [], "code": "00", "userid": 17}
responseData: {"data": [], "code": "00", "userid": 17}
requestData-4: {"token": "5e546324ba91858b26216d399dee33e1", "userid": 17}
type(requestData-4): <type ‘str‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: modify
文件截图:
至此,实现了获取请求数据,发送请求,把请求数据和响应数据写入文件
下一步,处理接口结果的校验,判断接口返回数据是否正确
步骤7:校验接口返回结果
在action包下新建check_result.py用于处理结果的校验
check_result.py:
#encoding=utf-8
import re
class CheckResult(object):
def __init__(self):
pass
@classmethod
def checkResult(cls,responseData,checkPoint):
# {"code":"00","userid":{"value":"w+"}, "articleId":{"type":"N"}}
# responseObj ={"code": "01", "userid": 12, "id": "12"}
errorKey={}
for key,value in checkPoint.items():
if isinstance(value,(str,unicode)):
#说明是等值校验
if responseData[key] != value:
errorKey[key] = responseData[key]
elif isinstance(value,dict):
#说明是需要通过正则表达式去校验
sourceData=responseData[key]#接口返回的真实值
if value.has_key("value"):
#说明是通过正则校验
regStr=value["value"]
rg=re.match(regStr,"%s" %sourceData)
if not rg:
errorKey[key] = sourceData
elif value.has_key("type"):
#说明是校验数据类型
typeStr=value["type"]
if typeStr == "N":
#说明是整形
if not isinstance(sourceData,(int,long)):
errorKey[key] = sourceData
return errorKey
if __name__ == ‘__main__‘:
resouceData={"code":"01","userid":12,"id":"12"}
checkPoint={"code":"00","userid":{"type":"N","id":{"value":"d+"}}}
print CheckResult.checkResult(resouceData,checkPoint)
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/action/check_result.py
{‘code‘: ‘01‘}
Process finished with exit code 0
修改主程序,添加写入校验结果部分
testScript.py:
#encoding=utf-8
import requests
import json
from util.ParseExcel import *
from util.httpClient import *
from config.config import *
from action.write_test_result import *
from action.check_result import *
def testInterface():
parseE = ParseExcel()
parseE.loadWorkBook(file_path)
sheetObj = parseE.getSheetByName(u"API")
activeList = parseE.getSingleColumn(sheetObj, API_ifExecute)
for idx, cell in enumerate(activeList[1:], 2):
if cell.value == ‘y‘:
rowObj = parseE.getSingleRow(sheetObj, idx)
apiName = rowObj[API_apiName - 1].value
requestUrl = rowObj[API_requestUrl - 1].value
requestMethod = rowObj[API_requestMethod - 1].value
paramsType = rowObj[API_paramsType - 1].value
apiTestCaseFileName = rowObj[API_apiCaseSheetName - 1].value
# print apiName,requestUrl,requestMethod,paramsType,apiTestCaseFileName
# 下一步读sheet表,准备执行测试用例
print "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
print "apiTestCaseFileName:", apiTestCaseFileName
caseSheetObj = parseE.getSheetByName(apiTestCaseFileName)
caseActiveObj = parseE.getSingleColumn(caseSheetObj, CASE_ifExecute)
for c_idx, col in enumerate(caseActiveObj[1:], 2):
if col.value == ‘y‘:
caseRowObj = parseE.getSingleRow(caseSheetObj, c_idx)
relyRule = caseRowObj[CASE_relyRule - 1].value
checkPoint=json.loads(caseRowObj[CASE_checkPoint-1].value)
print "relyRule:", relyRule
if relyRule:
# 发送接口请求之前,先获取请求数据
requestData = json.dumps(GetRequestData.getRequestData(eval(relyRule)))
print "requestData-2:", requestData
else:
requestData=caseRowObj[CASE_requestData-1].value
print "requestData without relyRule:",requestData,type(requestData)
hc = HttpClient()
print "requestMethod, requestUrl, paramsType, requestData:"
print requestMethod, requestUrl, paramsType, requestData
response = hc.request(requestMethod=requestMethod,
requestUrl=requestUrl,
paramsType=paramsType,
requestData=requestData
)
print "#############response.text##############:",response.text
if response.status_code ==200:
responseData=response.text
print "responseData-1,type(responsedata-1):",responseData,type(responseData)
errorKey=CheckResult.checkResult(json.loads(responseData),checkPoint)
write_result(parseE,caseSheetObj,c_idx,requestData=str(requestData),responseData=str(responseData),errorKey=errorKey)
else:
print "接口请求异常,状态码为:",response.status_code
else:
print "接口用例被忽略执行"
else:
print "API测试被忽略"
if __name__ == "__main__":
testInterface()
结果:ok
C:Python27python.exe D:/test/interfaceFramework_practice1/testScripts/testScript.py
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: register
relyRule: None
requestData without relyRule: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"} <type ‘unicode‘>
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/register/ form {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
#############response.text##############: {"username": "xufengchai6", "code": "01"}
responseData-1,type(responsedata-1): {"username": "xufengchai6", "code": "01"} <type ‘unicode‘>
requestData-4: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
type(requestData-4): <type ‘str‘>
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: login
relyRule: {"username":"userRegister->1->request","password":"userRegister->1->request"}
key:value==> username : userRegister->1->request
len(rList): 3
contentStr: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘xufengchai6‘, u‘password‘: u‘xufengchai121‘, u‘email‘: u‘[email protected]‘}
key:eval(command): username xufengchai6
requestData-1: {‘username‘: u‘xufengchai6‘}
key:value==> password : userRegister->1->request
len(rList): 3
contentStr: {"username":"xufengchai6","password":"xufengchai121","email":"[email protected]"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘username‘: u‘xufengchai6‘, u‘password‘: u‘xufengchai121‘, u‘email‘: u‘[email protected]‘}
key:eval(command): password xufengchai121
requestData-1: {‘username‘: u‘xufengchai6‘, ‘password‘: ‘7f73a2e4d8b01b0f0f1062a59d4df635‘}
requestData-2: {"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/login/ form {"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
#############response.text##############: {"token": "c7cf5ab52e674f2449f22579d294426d", "code": "00", "userid": 2, "login_time": "2018-08-17 21:27:10"}
responseData-1,type(responsedata-1): {"token": "c7cf5ab52e674f2449f22579d294426d", "code": "00", "userid": 2, "login_time": "2018-08-17 21:27:10"} <type ‘unicode‘>
requestData-4: {"username": "xufengchai6", "password": "7f73a2e4d8b01b0f0f1062a59d4df635"}
type(requestData-4): <type ‘str‘>
API测试被忽略
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
apiTestCaseFileName: searchUserBlog
relyRule: {"token":"userLogin->1->response","userid":"userLogin->1->response"}
key:value==> token : userLogin->1->response
len(rList): 3
contentStr: {"token": "c7cf5ab52e674f2449f22579d294426d", "code": "00", "userid": 2, "login_time": "2018-08-17 21:27:10"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘c7cf5ab52e674f2449f22579d294426d‘, u‘code‘: u‘00‘, u‘userid‘: 2, u‘login_time‘: u‘2018-08-17 21:27:10‘}
key:eval(command): token c7cf5ab52e674f2449f22579d294426d
requestData-1: {‘token‘: u‘c7cf5ab52e674f2449f22579d294426d‘}
key:value==> userid : userLogin->1->response
len(rList): 3
contentStr: {"token": "c7cf5ab52e674f2449f22579d294426d", "code": "00", "userid": 2, "login_time": "2018-08-17 21:27:10"}
type(contentStr): <type ‘unicode‘>
relyContent: {u‘token‘: u‘c7cf5ab52e674f2449f22579d294426d‘, u‘code‘: u‘00‘, u‘userid‘: 2, u‘login_time‘: u‘2018-08-17 21:27:10‘}
key:eval(command): userid 2
requestData-1: {‘token‘: u‘c7cf5ab52e674f2449f22579d294426d‘, ‘userid‘: 2}
requestData-2: {"token": "c7cf5ab52e674f2449f22579d294426d", "userid": 2}
requestMethod, requestUrl, paramsType, requestData:
post http://39.106.41.29:8080/getBlogsOfUser/ form {"token": "c7cf5ab52e674f2449f22579d294426d", "userid": 2}
#############response.text##############: {"data": [], "code": "00", "userid": 2}
responseData-1,type(responsedata-1): {"data": [], "code": "00", "userid": 2} <type ‘unicode‘>
requestData-4: {"token": "c7cf5ab52e674f2449f22579d294426d", "userid": 2}
type(requestData-4): <type ‘str‘>
API测试被忽略
Process finished with exit code 0
Excel结果:写入请求数据、响应数据、结果ok
由于这种方式没有用到responseCode和dataStore列,所以删掉了,之后换种思路时会用到,到时再加上
以上是关于搭建接口自动化测试框架的思路分析和技术点的主要内容,如果未能解决你的问题,请参考以下文章
API接口自动化测试框架搭建(二十六)-框架README.md设计