框架的流程,个人是觉得有2个步骤比较有难度的,可以拿出来详细记录一下,毕竟就算自己做的东西,时间久了也是会忘记。
一、请求体数据量太大,应该怎么存放,如何实现代码、数据分离:
问题:
如果测试的接口是登录、注册类似的接口,请求体可能就是一行就够了,但是工厂后台的接口,大部分都是素材相关的接口,请求体内容很大,一般都有100-200行之间,下面是一个接口的请求体,展开看看130行左右。。
{
"ImagePath": "/UpFile/C00003374/PMC/DesignMaterial/202009/9a76277bbe194ff8be5410b2c483331b/lALPBE1XYgg7RTDMyszK_202_202的副本3.png",
"MaterialFileIds": [
{
"FilePath": "/UpFile/C00003374/PMC/DesignMaterial/202009/6dd6332d8f7d4d4ba7ae7348e3394017/lALPBE1XYgg7RTDMyszK_202_202的副本3.png",
"FileBaseId": "1084968463",
"Status": "0",
"IsMainPic": false,
"Files": []
},
{
"FilePath": "/UpFile/C00003374/PMC/DesignMaterial/202009/6eead46ddee14da991cefa39d219b2cf/lALPBE1XYgg7RTDMyszK_202_202的副本8.png",
"FileBaseId": "1084968602",
"Status": "0",
"IsMainPic": false,
"Files": []
},
{
"FilePath": "/UpFile/C00003374/PMC/DesignMaterial/202009/9a76277bbe194ff8be5410b2c483331b/lALPBE1XYgg7RTDMyszK_202_202的副本3.png",
"FileBaseId": "1084978639",
"Status": "0",
"IsMainPic": true,
"Files": []
}
],
"MaterialId": "113971677",
"ProductId": "32553334",
"MaterialName": "autotest",
"PicWidth": "333",
"PicHeight": "333",
"Model": "333",
"MaterialAttribute": "TilesCategory",
"Property": "CeramicTile_Old",
"description": "",
"BindBrandSeries": {
"BrandId": 100018819,
"SeriesId": 100019718,
"CategoryId": 6498114,
"MaterialIds": [
"113971677"
],
"CombineState": 0
},
"RetailPrice": "333",
"Is3d": 1,
"IsPublic": 0,
"materialBallType": 1,
"Style": "15878",
"Color": "19654",
"Label": [
{
"CategoryId": "100019718",
"CategoryName": "123456",
"LableType": 4,
"Status": "0"
},
{
"CategoryId": "15878",
"CategoryName": "现代",
"LableType": 2,
"Status": "0"
},
{
"CategoryId": "19654",
"CategoryName": "米色",
"LableType": 1,
"Status": "0"
}
],
"CategoryId": "9310643",
"MixPaveSetting": "MPS_MultiBrickPave",
"SpecialTexture": "SpecialTexture_Vertical",
"FileList": [
{
"RecordID": "33192222",
"AuxiliaryMaterialId": "113973591",
"AuxiliaryMaterialName": "lALPBE1XYgg7RTDMyszK_202_202的副本3.png",
"AuxiliaryImagePath": "/UpFile/C00003374/PMC/DesignMaterial/202009/6dd6332d8f7d4d4ba7ae7348e3394017/lALPBE1XYgg7RTDMyszK_202_202的副本3.png",
"AuxiliaryImagePathId": "1084968463",
"Status": "0"
},
{
"RecordID": "33189918",
"AuxiliaryMaterialId": "113971678",
"AuxiliaryMaterialName": "lALPBE1XYgg7RTDMyszK_202_202的副本8.png",
"AuxiliaryImagePath": "/UpFile/C00003374/PMC/DesignMaterial/202009/6eead46ddee14da991cefa39d219b2cf/lALPBE1XYgg7RTDMyszK_202_202的副本8.png",
"AuxiliaryImagePathId": "1084968602",
"Status": "0"
}
],
"MaterialExtensionID": "3736615",
"IsHasProduct": 1,
"BrickId": "31603",
"IsBumpMap": 0,
"Product": {
"ProductName": "autotest",
"Style": "15878",
"CostPrice": "33",
"RetailPrice": "333",
"WholesalePrice": "333",
"Color": "19654",
"Unit": "bucket",
"Detail": "%3Cp%3E8888%3Cimg%20src%3D%22/upfiles/image/20200924/6373657762525056354508274.png%22%20title%3D%22lALPBE1XYgg7RTDMyszK_202_202%u7684%u526F%u672C4.png%22%20alt%3D%22lALPBE1XYgg7RTDMyszK_202_202%u7684%u526F%u672C4.png%22/%3E%3C/p%3E",
"IsYunhuojiaProduct": 0
},
"ProductIdToPrices": [
{
"RecordId": "1013976",
"Code": "8",
"ProductLevel": "8",
"CostPrice": "8.00",
"RetailPrice": "8.00",
"Price": "8.00",
"Status": "0"
}
],
"ProductImageFile": [
{
"FilePath": "/UpFile/C00003374/PMC/Product/202009/CC6397F6DA454017BCBB220BEAFDBF43/CC6397F6DA454017BCBB220BEAFDBF43_130x100.png",
"FileId": "1084977107",
"Status": "0"
}
],
"External": {
"LinkUrl": "外链",
"ExternalId": "2138211"
},
"bindTaoBaoProduct": {
"item": "600231027677",
"sku": "4220891757703",
"wholesalePrice": "333",
"untying": false,
"status": 0
}
}
需要实现大批量的接口自动化,这些接口的请求体数据应该怎么存放,而且,每个接口请求体的格式都不一样,那就做不了一份公共的数据公用所有接口,只能一个接口URL对应一个请求体。
前期,为了先实现功能,直接把请求体写死在代码里面,每次发版之前,在预发布环境跑一次,需要维护、变更的是时候,基本不想去动,因为真的毫无美感。。
解决方案:
有一段时间考虑过,想要直接从kibana里面取接口的请求体出来,因为应用的log都有写到es,但是,kibana数据量巨大,很难搜索到符合条件的测试数据,而且kiabna如果有问题,自动化测试就会全部报错,所以,这个方案也不是最好。
也测试过,网上很多同行的方法,把请求体放到Excel表格里面,如果是用户名、密码这些或者几行的请求体是可以的,但是把一个100多行的数据放到一个小格子里面,想一想也知道,看也不好看,改也难改,而且,本人也不喜欢Excel处理数据。
还有yaml的数据格式的,也尝试了,问题是,每次我想改数据,还需要研究一番,我的数据应该用什么样的格式改,但是,我的需求是,只想改完就走,越快越好,这明显不符合我的需求。而且,yaml的格式在python里面调用,还需要转为字典格式,更加增加了麻烦。
还有一种方案,数据存放在数据库,网上的方案都是用的mysql数据库,mysql数据库是关系型数据库,如果要把100多行的json字段写进去,就需要创建很多的字段,而且,不同的请求体数据,字段不一样,在一张表里面数据不统一,看起来就很乱,也不合适。
网上谷歌看了很多了也没有找到可以真正满意的方案,那一段时间忙于订单系统迁移、工厂后台版本测试工作量繁重,慢慢就放一边了。
直到一天,刷公众号,看到了一篇推文,提到MongoDB:
我的场景不也刚刚好符合吗,MongoDB是非关系型数据库,和mysql不一样,MongoDB的Bson和JSon格式的数据十分适合文档格式的存储与查询,而工厂后台接口的请求体的格式就是json的。
周末想起来这个事,就搞了起来,也是终于把MongoDB成功搭建到公司的机器上面了,经验教训:Linux系统装所有的服务,能用docker安装,绝对不装在宿主机,不然估计还肝几个通宵都不一定搞掂,环境出现的各种问题是不可预测的,
具体搭建过程记录如下:https://blog.csdn.net/zhoujunjunlove/article/details/108749683
实际效果如下:
把请求体的json数据和urlpath写到一个json里面,存放到MongoDB,测试用例调用pymongo库获取数据库的body数据,成功实现代码、数据分离。
import pymongo
from git_3weijia.ApiAutoTest.FmbApiAutoTest.conf.config import *
class FatchApiData():
def __init__(self, host):
# 从config文件读取数据库IP/port
self.host = list(host.keys())[0]
self.port = int(host[list(host.keys())[0]])
# 创建一个pymongo对象
self.connection = pymongo.MongoClient(self.host, self.port)
# 连接mongo数据库fmb
self.tdb = self.connection.fmb
# 连接mongo数据库fmb的fmbapidata表
self.collection = self.tdb.fmbapidata
# 插入数据到mongo表
def insert_mongo_one(self):
dictdata = {"test": "demo"}
self.collection.insert(dictdata)
print(' insert dictdata done ')
# 关闭数据库连接
self.connection.close()
# 查找数据库mongo表数据
def select_mongo_one(self, url=None):
result = self.collection.find_one({"urlpath": url})
# print(result["body"])
# 关闭数据库连接
self.connection.close()
return result
if __name__ == '__main__':
a = FatchApiData(db_host)
a.select_mongo_one("pmc/category/saveCategory")
# a.insert_mongo_one()
二、如何组织接口测试用例,用何种设计模式,做到公共代码复用率高,减少重复代码,提高开发效率
工厂后台接口有几个特点:
1.请求体的格式不是统一的
2.响应结果的数据格式不是统一的
3.要符合业务场景,比如一个素材增删改查
这3个特点,就决定了,不能只用数据驱动来执行测试用例,就是,100个接口,只写一个test用例,用100份数据来驱动测试,这是行不通的,
因为100个接口的请求头accpt要求有json格式、form格式,响应结果格式也都不完全一样,而且要符合业务场景,就要下一个接口用例接收上一个用例返回来的值。
问题:
开发100个接口自动化测试用例,后续维护继续增加,慢慢可能就500、1000、2000,这些用例应该怎么存放?
解决方案:
1.按照系统的业务模块划分,一个模块对应一个文件夹;
2.按照系统的功能划分,一个功能对应文件夹里面的一个代码文件的测试类;
3.按照系统的模块-功能-接口细分,一个接口对应一个类里面的test方法
这样组织划分后,一个接口就对应测试报告的一行,如果报错了,可以快速找到报错的用例,后续系统业务变更,对应修改也方便。
问题:
这样就会出现另外一个问题,如果每个接口都需要写一个test方法,编写批量接口就会导致大量重复的代码,降低开发效率,所以,还需要考虑减少重复代码,提高开发效率。
解决方案:
正常来说,一般开发过程中就会考虑这个问题,我在写了10个接口的test方法后,就考虑应该怎么样把接口重复的代码去掉,有意思的是,在按照自己的想法去做优化后,回头看,其实就是面向对象开发的三大基本特征:封装、继承、多态。但是,在优化的时候是没有这个想法,说怎么做封装、继承、多态,而是在优化完了之后,才发现这些优化就是封装、继承、多态。而站在面向对象开发的三大基本特征的更高一个层次来看,应该就是工厂设计模型了。
工厂设计模型的原理可以看下:https://blog.csdn.net/zhoujunjunlove/article/details/109166882
简单说就是,把一个方法定义在基类里面,不去实现功能,在子类继承这个方法,再去实现功能。这种在父类里面定义接口(方法名就是接口的意思), 在子类实现这个方法,这种叫做工厂方法设计模型。
工厂模式在自动化测试中的应用
部分代码(比如获取MongoDB数据)是基本所有测试用例都需要写的,就定义一个基类publicObject,来实现这个方法,让子类apiObject、schemeObject、categoryObject继承这个方法;
而apiObject、schemeObject、categoryObject类代表不同模块的测试用例类的父类,在父类里面定义模块公用的方法,再让测试用例的类继承这些类;
这样,父类、父父类定义了方法,而功能是在测试类才实现,这样就可以把大量重复的代码放在父类、父父类,让测试类继承调用就可以。
比如:
父父类定义公共方法
父类继承父父类并定义新的方法
子类继承父类,并调用父父类的方法和父类的方法
这样一个test方法的接口用例,只需要几行代码就可以实现了