最近复原了之前公司的一套测试框架,并根据我们业务组的实际情况,进行一些变动和调整,之前公司业务流程比较长,多了一个业务层,做了四层封装,我们这边暂时的需求业务线没有这么长,后续可以根据需求在模板层的基础上适当扩展一层,目前在CRM1.16的接口测试阶段实现并应用了此框架~
简介:框架采用三层封装,基础层和模板层为继承关系,模板层之间相互独立,也可以互相调用,用例层相互独立。现阶段主要应用于CRM的需求,最底层(base)的封装本业务线各个需求都需要用的一些共用的方法,比如各种请求,鉴权,数据库连接封装等,主要是crm相关的业务逻辑;中间层(model)的封装是在各个接口的基础上封装请求过程,默认参数,断言以及回参结果验证,主要是各个需求的相关接口,可以每个需求新增一个;最上层(case)的封装专注于各种场景的测试以及复杂逻辑的测试,根据model适当增加;
结构图:
优点:
可扩展性:每一个需求相关接口编写一个model层,case层可以调用不同的model层来组合复杂场景;
可维护性:三层封装的结构,使得在接口的调用,同一个接口只有一个入口,并且实施读取swagger的接口参数,即使有接口有大的变动也可以只改一个接口的函数,就可以维护所有相关case,而且不影响其他接口调用;
兼容性:python本身的兼容性就比较强,可以对接各种平台,比如结果可以回写testlink,接口调用可以接入Jenkins,只需要个人提交代码到SVN,所有人可以在发版之前在Jenkins平台运行一遍所有的用例;
灵活性:接口用例用代码来写本来就具有很高的灵活性,case层可以采用ddt数据驱动的方式,在装饰器里添加参数,就可以快速添加多条各种场景的用例,model层也可以在接口的调用中,加入一些判断来处理调用接口的各种情况;
稳定性:model层加容错处理来处理接口调用的各种情况,case层添加setup和setdown来作为前置和后置处理器,清理会对接口测试造成影响的参数,尽量减少每个case之间的依赖性,使之独立;
可靠性:在model层加入数据的校验,测试环境可以加入数据库的校验,而不仅仅只是校验一个code和message,现网的环境可以适当放宽,不用做数据库的校验;
缺点:
需要一定的编程基础,可能导致前期的接口测试周期变长;每个人写的model风格不一,互相调用不熟悉的模块可能导致脚本容易报错;
解决办法:
适当增长接口测试周期;model层统一的接口封装的风格;
另外:框架的思路是根据UI框架的page object的模式衍生而来的
代码详情:
最底层(base)
# coding:utf8
import requests,json,re,pymysql,binascii,yaml,hashlib,time
from urllib import parse
class EC_base():
def post(self, url, data ,debug = False, header = {}):
self.header['Content-Type'] = 'application/x-www-form-urlencoded'
for key in header.keys():
self.header[key] = header[key]
if self.header[key] == 'application/json':
data = json.dumps(data)
if debug:
print(url)
print(data)
print(self.header)
print(self.s.cookies)
content = self.s.post(url, data=data, headers=self.header)
if debug:
print(content.text)
if 'ec_csrf_token' in content.cookies:
self.cookies_ec_csrf_token = content.cookies['ec_csrf_token']
self.header['ec_csrf_token'] = self.cookies_ec_csrf_token
if 'XSRF-TOKEN' in content.cookies:
self.CRMAPI_TOKEN = content.cookies['XSRF-TOKEN']
try:
content = content.text
content = json.loads(content)
except:
pass
return content
def get(self, url, params = None, debug = False, header = {}):
self.header['Content-Type'] = 'application/x-www-form-urlencoded'
if debug:
print(url)
print(self.header)
print(self.s.cookies)
for key in header.keys():
self.header[key] = header[key]
content = self.s.get(url,params= params, headers=self.header)
if debug:
print(content.text)
try:
content = content.text
return json.loads(content)
except:
pass
return content
def delete(self, url, params = None, debug = False, header = {}):
self.header['Content-Type'] = 'application/x-www-form-urlencoded'
if debug:
print(url)
print(self.header)
print(self.s.cookies)
for key in header.keys():
self.header[key] = header[key]
content = self.s.delete(url,params= params, headers=self.header)
if debug:
print(content.text)
try:
content = content.text
return json.loads(content)
except:
pass
return content
def yaml(self):
return yaml.load(open("xxx/1.yaml"))
def login(self):
#登陆并鉴权
self.s = requests.session()
self.header = {}
#获取登陆信息
......
def mysql_login(self, corp_id, db_type = 1):
#数据库信连接
if db_type == 0:
#BASE基础库
host, prot = 'xx.xx.xx', xxxx
user = password = 'ecuser'
db = pymysql.connect(host, user, password, None, prot)
return db
def MD5(self, str1):
h1 = hashlib.md5()
h1.update(str1.encode(encoding='utf-8'))
return h1.hexdigest()
def api_data(self):
#通过swagger获取接口数据
self.urls = {}
groups = [移动端","企业管理","客户端"]
group_names = ["移动端","企业管理","客户端"]
for i in range(len(groups)):
group_str = parse.quote(groups[i])
group_name = group_names[i]
self.urls[group_name] = {}
a = requests.session().get('url')
data = json.loads(a.text)
host = "https://" + data['host']
for tags in data['tags']:
self.urls[group_name][tags["name"]] = {}
for paths in data["paths"].keys():
for tags in data["paths"][paths].keys():
for name in self.urls[group_name]:
type= data["paths"][paths][tags]
if type["tags"][0] == name:
self.urls[group_name][name][type["summary"]] = host+paths
if __name__ == "__main__":
pass
中间层(model)
from test.ec import EC_base
class Field_model(EC_base):
def __init__(self):
self.api_data()
self.login()
content = self.getFieldInfo()
self.groupID1 = content["data"]["groups"][0]["id"]
self.name1 = content["data"]["groups"][0]["name"]
self.groupID2 = content["data"]["groups"][1]["id"]
self.name2 = content["data"]["groups"][1]["name"]
def getFieldInfo(self):
content = self.get(self.urls['企业管理']['客户字段']['字段信息列表'])
return content
def addGroup(self,name,code=200):
#添加分组
content = self.post(self.urls['企业管理']['客户字段']['添加分组'],{"name":name})
assert content['code'] == code ,"返回报错:" + content['msg']
if code == 200:
id = content['data']['id']
name = name.strip()
sql = "..."
self.query_data(sql, 1)
return id
def addField(self,name="python测试", id=0, groupId = None, type = 1,data_num = 1,
isMust=1, status = 1,params =[],code = 200, funtion_type = "添加自定义字段及选项"):
#添加自定义字段
header = {'Content-Type': 'application/json'}
if not groupId:
groupId = self.groupID2
data = {"groupId": groupId,
"id": id,
"isMust": isMust,
"name": name,
"params": params,
"status": status,
"type": type}
content = self.post(self.urls['企业管理']['客户字段'][funtion_type], data, header=header)
assert content['code'] == code, "返回报错:" + content['msg']
if code == 200:
id = content['data']['id']
name = name.strip()
sql = " xxxxxx"
self.query_data(sql, data_num)
return id
def query_data(self,sql, num=None):
db = self.mysql_login(self.corp_id)
cursor = db.cursor()
#验证分组数据
cursor.execute(sql)
data = cursor.fetchall()
db.close()
if num:
assert len(data) == num ,"测试数据与数据库对不上!查询语句:" + sql
else:
return data
if __name__ == '__main__':
pass
最上层(case)
from test.main.model.Field import Field_model
from ddt import ddt,data,unpack
import unittest
@ddt
class Field_case(unittest.TestCase):
@classmethod
def setUpClass(self):
self.model = Field_model()
def setUp(self):
try:
content = self.getFieldInfo()
for group in content["data"]["groups"]:
if group['name'] == "python测试":
self.model.deleteGroup(group['id'])
for field in group['fields']:
if field['name'] == "python测试":
self.model.deleteField(field['id'])
except:
pass
@data(-1, 1, 2, 3, 4)
def test_01(self,value):
#高级搜索-获取分组字段列表
self.model.searchField(value)
@data("python测试", "测试10个字哈哈哈哈", "+-*%$*.,", "null", "测试 空格"," 测试空格","测试空格 ")
def test_02(self, value):
#【成功】添加/删除分组
id = self.model.addGroup(value)
self.model.deleteGroup(id)
def test_03(self):
#【失败】添加分组重复
id = self.model.addGroup('测试重复A')
self.model.addGroup('测试重复A',code=40102)
self.model.addGroup('测试重复a', code=40102)
self.model.addGroup('基本信息', code=40102)
self.model.deleteGroup(id)
@data(["", 4001], ['测试超10个字哈哈哈哈', 40101])
@unpack
def test_04(self, value, code):
#【失败】添加分组-分组名不合法
self.model.addGroup(value, code=code)
if __name__ == '__main__':
Field_case.main()