专栏文章 质量保障系统的落地实践 (一) 概览篇
专栏文章 质量保障系统的落地实践 (二) 项目管理设计 - 基础信息与缺陷信息设计
专栏文章 质量保障系统的落地实践 (二) 项目管理设计 - 代码信息设计
专栏文章 质量保障系统的落地实践 (三) CI 管理设计 - 基础设计
专栏文章 质量保障系统的落地实践 (三) CI 管理设计 - 集成设计
书接上文, CI 管理设计 - 集成设计篇介绍了质量保障系统如何与自动化框架建立有效关联,本篇文章就在这个基础上做发散介绍。在实际测试过程中,往往有一定的造数需求—通过自动化脚本,创造出符合测试所需的数据。可见造数脚本也是需要使用自动化脚本编程的,但同时造数脚本又不同于自动化校验校验。一来造数脚本往往不需要校验逻辑,只需要执行创建数据即可;二来造数脚本往往需要具备根据外部传参来生成满足期望的数据,而不全是随机数据。将积累下来的造数脚本统一管理起来,就是接下来我们要谈谈的造数工厂。
由于造数脚本需要依据外部需求来改变生成的数据属性,那么该怎么处理?举一个具体的例子,比如场景上需要生成一个具备姓名、身份证、性别的用户,若是没有指定姓名、身份证、性别,则随机生成;若是外部指定姓名、身份证、性别,则使用外部的数据,那么伪代码的形式:
def DiyPerson(name=None, idcard=None, sex=None):
if name is None:
name = randomName()
if idcard is None:
idcard = randomIdCard()
if sex is None:
sex = randomSex()
data = {
"name": name,
"sex": sex,
"idcard": idcard
}
response = requests.post(url, json=data)
上述方式是比较常见的处理思路,所有变量均设置一个默认空值,若是没有传递,则随机生成数据。只是编写相对比较繁琐,特别是遇上表单项特别多的情况下,那么该怎么处理比较合适?大家注意到最后发起接口请求的时候,需要拼接data主体,里面包含了所有数据内容,是不是只要想办法把 data 的数据替换了就可以了:
def repalceRequestData(request_data, replace_data):
data_ = {}
for key, value in request_data.items():
if key in replace_data:
value = replace_data[key]
data_[key] = value
return data_
def DiyPerson(replace_data={}):
name = randomName()
idcard = randomIdCard()
sex = randomSex()
data = {
"name": name,
"sex": sex,
"idcard": idcard
}
data_ = repalceRequestData(data, replace_data)
response = requests.post(url, json=data_)
使用外部传递的 replace_data 属性,在接口调用前完成属性替换。那么可能有人要问,这么写的话,name、idcard、sex 属性的赋值动作重复了,不是会产生执行效率浪费吗?确实,这个方案会有一定执行效率的损耗,但是也有个明显的好处,就是改造成本很小。一般来说在设计自动化脚本的时候,为了脚本能够多次执行,很多数据往往采用随机生成的方案,如上述例子中的身份证、为避免身份证重复,所以代码中往往不会写死某一数据,所以自动化脚本的编写模板一般如下:
def DiyPerson():
name = randomName()
idcard = randomIdCard()
sex = randomSex()
data = {
"name": name,
"sex": sex,
"idcard": idcard
}
response = requests.post(url, json=data)
大家可以对比一下传入 replace_data 的方案与自动化脚本编写的模板方案的差别,就可以很直观地看到使用 replace_data 的造数脚本方案只需要在自动化脚本的基础上完成少量改造即可,即:1、调用repalceRequestData方法;2、传入 replace_data。按照这样的方案编写造数脚本的效率将会大幅提升。
在 CI 管理的部分,我们提到了基于不同公司基建的处理方法,此处也是类似。若是公司有完整的执行平台,我们就可以借助这个平台完成造数脚本的触发;若是公司不具备条件,那么我们就通过命令行自行触发造数脚本。有兴趣的小伙伴可以翻看专栏文章 质量保障系统的落地实践 (三) CI 管理设计 - 集成设计。
谈完了造数脚本的触发,接下来很自然的就会想到触发后的结果如何反馈了。还是以上述的例子来讲解,我们通过脚本定制了一个名叫张三、性别为男的用户信息,那么这条数据是否生成,生成后用户的 ID 等信息如何告知调用者?这又是一个问题。
若是公司具备自动化执行平台,虽然可以解决调用的问题,但是同时会带来另一个问题,这类平台的执行结果反馈往往是通用模板,并不能完全满足造数需求的反馈:
若是公司不具备自动化执行平台,那么就更无从说起执行反馈的情况了。那么该怎么办?
解决方案就是我们自行补全这部分能力,我们编写一套脚本,待执行完成后,将反馈给质量保障平台。接下来还是以具体的例子来说明:
1、首先我们需要创建用户之后,自定义生成通知我们的消息体:
data = {
"name": name,
"sex": sex,
"idcard": idcard
}
data_ = repalceRequestData(data, replace_data)
response = requests.post(url, json=data_)
try:
response = response.json()
userId = response["data"]["id"]
msg = f"创建用户成功,传参:{data_},用户id:{userId}."
success = true
except json.JsonDecodeError:
msg = f"创建用户失败,传参:{data_}."
success = false
except KeyError:
msg = f"创建用户失败,传参:{data_},接口响应:{response}."
success = false
# 至此我们可以标记出此次执行是否成功,以及成功或失败的具体信息,将信息存储到文件中
with open(result.json, "w") as f:
item = {
"success": success,
"msg": msg
}
f.write(json.dumps(item))
2、其次已经将消息存储在了 result.json 文件中,那么接下来只需要解析这个文件,想办法把这个文件的 json 内容回调给质量保障平台即可。若是有执行平台,这类平台往往使用 jenkins 等集成方案,允许加入执行脚本,那么只要将解析 result.json 文件,调用质量保障平台的接口的脚本嵌入就能完成反馈回调;若是没有执行平台,那么就更方便处理了,举个例子,如果执行脚本的命令为:python3 userFlow.py,那么我们只需要在命令后拼接上执行回调函数的命令即可:python3 userFlow.py;python3 callback.py,由于命令行是顺序执行的,所以命令:python3 userFlow.py;python3 callback.py将保证完成造数脚本执行,反馈信息写入 result.json 文件后,再执行 callback.py 的脚本,这样我们就可以在这个 callback.py 文件中处理读取 json 文件,回调质量保障平台的逻辑:
# callback.py
with open(result.json,) as f:
content = json.loads(f.read())
response = requests.post(url, json=content)
3、最后质量保障平台处理回调数据:
msg = data["msg"]
success = data["success"]
if success:
notify_msg = f"造数执行成功,执行信息:{msg}."
else:
notify_msg = f"造数执行失败,执行信息:{msg}."
# 通知调用方
dingTalkNotify(webhook, notify_msg)
既然完成了整个造数工厂的搭建,就要考虑数据沉淀的问题了,毕竟没有数据支撑,价值很难体现。既然打通了整个造数逻辑,那么我们只需要在每个造数脚本上增加预估节约的造数时间成本,如 5 分钟、7 分钟的配置。在触发造数脚本之前,先保存一条造数执行日志记录,将状态置为进行中。当造数脚本完成回调质量保障平台时,将这条记录置为完成,统计时将所有完成状态的执行日志进行汇总,统计节约的时长,就可以得出节约人效。
# 效能管理-造数脚本配置
class DataFactoryConfigInfo(BaseModel):
department_id = models.IntegerField(verbose_name=u"归属组织节点")
name = models.CharField(max_length=150, verbose_name=u"造数脚本名称")
per_period = models.IntegerField(default=0, verbose_name=u"单次节约时间(分)")
owner_name = models.CharField(max_length=11, verbose_name=u"负责人姓名")
owner_phone = models.CharField(max_length=11, verbose_name=u"负责人手机号")
class Meta:
db_table = "Data_Factory_Config_Info"
verbose_name = "效能管理-造数脚本配置"
verbose_name_plural = verbose_name
# 效能管理-造数脚本执行日志
class DataFactoryLogInfo(BaseModel):
department_id = models.IntegerField(verbose_name=u"归属组织节点")
config_id = models.IntegerField(verbose_name=u"造数脚本配置id")
name = models.CharField(max_length=150, verbose_name=u"造数脚本名称")
period = models.IntegerField(default=0, verbose_name=u"节约时间(分)")
owner_name = models.CharField(max_length=11, verbose_name=u"负责人姓名")
owner_phone = models.CharField(max_length=11, verbose_name=u"负责人手机号")
env = models.CharField(max_length=20, verbose_name=u"执行环境")
is_done = models.BooleanField(default=False, verbose_name=u"是否执行完成")
caller_name = models.CharField(null=True, blank=True, max_length=11, verbose_name=u"调用者姓名")
caller_phone = models.CharField(null=True, blank=True, max_length=11, verbose_name=u"调用者手机号")
response = models.TextField(null=True, blank=True, verbose_name=u"造数调用结果")
class Meta:
db_table = "Data_Factory_Log_Info"
verbose_name = "造数脚本执行日志"
verbose_name_plural = verbose_name
本篇文章基于 CI 管理部分进行的拓展介绍。若是能完全理解这些设计思路,那么我们测试人员搭建质量保障平台的能力将会得到一定的提升,可以不那么依赖现成的基建能力,更不会受制于基建能力,希望能够给大家一些帮助,谢谢。