草庐IT

高复用性自动化脚本设计实践

京东云开发者 2023-03-28 原文
作者:京东物流 刘红妍

导读:在自动化测试实践中,为了更好的契合被测业务场景,需要不断优化框架分层结构。本文结合产品模块化思路,意在介绍通过策略模式改造原本复杂分支语句代码,通过理论讲解、思路分析、方案设计、及代码演示,提供自动化脚本重构的落地方案。

01 痛点

在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!

随着运输业务场景的不断丰富和自动化脚本量的不断累积,日常在review用例时发现,目前大家仍停留在针对需求定制化用例编写,无法提高用例可复用性和可编排性。当业务流程中间某一环节发生变化时,不但需要重新修改脚本,还会影响当前应用其他用例执行结果。所以,如何设计高复用性脚本成为目前自动化建设的关键节点。

02  设计理论*

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。从设计稿出发,提升页面搭建效率,亟需解决的核心问题有:

2.1 设计理念*        根据面向对象程序设计理念,设计者应遵循高内聚与低耦合原则,通常程序结构中各模块的内聚程度越高,模块间的耦合程度就越低。高内聚意味着一个类所能提供的功能应该是相关的,即一个类不要设计得包括很多互不相干的功能,低耦合代表要合理规划模块的颗粒度,即要保证一个模块可独立存在,降低模块之间复杂依赖关系。

2.2 策略模式

策略模式定义了一系列的算法,将每一组相关的算法封装起各个策略分支,从而将分支相关的代码隐藏起来,并且使它们之间可以相互替换。策略模式让算法的变化不会影响到使用算法的客户,希望可以提高程序的可扩展性。

03  解决思路

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。从设计稿出发,提升页面搭建效率,亟需解决的核心问题有:

3.1 基本思路

根据运输业务同一个流程存在不同场景,如询价服务接上游下发询价单节点,需要区分来源执行不同逻辑,目前设计五个算法能力,根据后期业务不断扩展,还会有更多算法加入进来,这个时候需要考虑一个好的结构对代码进行优化。可能前期大家通过if...elif...else 分支语句就可实现,但在考虑系统的健壮性和可维护性,这里就不能大量使用if分支语句。因为每一种算法能力的代码量极大且算法参数几十个,在随着更多上游接入可能存在十几个甚至更多else分支,很容易顾此失彼,牵一发而动全身。所以,利用策略模式设计一系列算法,再供用例拼装调用,提高代码的可读性和可复用性。

3.2 方案分析

优点:

  1. 代码解耦,便于维护;
  2. 避免使用难以维护的多重条件选择语句;
  3. 可以运行时动态切换算法;
  4. 开闭原则。无须对上下文代码进行修改,就可以添加新的代码。
缺点:

  1. 如果算法逻辑,较为固定,不经常修改,使用策略模式只会增加代码量
  2. 必须知道所有的具体策略类及它们的区别。
04  方案概述

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。

4.1 环境依赖

Laputa框架简介:

Laputa框架基于 Pytest 集成了对API接口自动化, 以及对 Web应用, 移动端应用和 Windows 桌面应用 UI 等自动化的能力。具有可视化的Web界面工具, 便于配置执行规则,关联执行脚本, 触发用例执行,查看执行结果。提供CI集成服务,调用Jenkins API跟踪持续集成结果,开放接口,实现流水线自动化测试。

图1 自动化框架架构图

4.2 分层改造

图2 自动化用例分层图

4.3 策略设计

图3 策略模式设计图

4.4 操作步骤

  1. 将频繁修改的算法进行抽取,独立为具体的算法类;
  2. 创建抽象基类,实现一个约定的抽象策略方法;
  3. 所有独立的算法类,必须实现基类中的抽象策略接口;
  4. 建立上下类,该类可以动态的对算法进行setter,创建调用具体算法的方法,上下文可通过该方法与具体的策略交互;
  5. 客户端进行调用,传入具体的算法类,上下文动态执行具体的算法任务。
05  设计实践

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。

5.1 询价接单接口改造

如源代码结构,根据不同业务来源,写在一个方法里通过if...else...分别组装场景,一旦上游任一系统存在需求变动,当前接单接口调用逻辑需要变动:

【python】
def receive_enquiry_bill(**kwargs):
params=[{}]
params[0].update(kwargs)
if params[0].get("enquirySource") == 8:
pass
elif params[0].get("enquiryWay") == 2 and params[0].get("payMode") == 2:pass
elif params[0].get("enquiryWay") == 2 and params[0].get("payMode") == 3:pass
if params[0].get("enquirySource") == 46:
pass
if params[0].get("enquirySource") == 20:
pass
改造结构: 上下文类

【python】class AlgorithmStrategy(object):
def __init__(self, algorithm_name):
self.algorithm_name = algorithm_name

@property
def algorithm(self):
return self.algorithm_name

@algorithm.setter
def algorithm(self, name): self.algorithm_name = name

def execute_algorithm(self, params): return self.algorithm_name.execute(params)
算法基类

【python】
class CreateEnquiryBillBaseAlgorithm(ABC):# 算法能力基类
@abstractmethod
def read_params(self,
**kwargs):
scenario=kwargs['scenario'] if "scenario" in kwargs and kwargs['scenario'] else 【python】
def receive_enquiry_bill(**kwargs):
params=[{}]
params[0].update(kwargs)
if params[0].get("enquirySource") == 8:
pass
elif params[0].get("enquiryWay") == 2 and params[0].get("payMode") == 2:
pass
elif params[0].get("enquiryWay") == 2 and params[0].get("payMode") == 3:
pass
if params[0].get("enquirySource") == 46:
pass
if params[0].get("enquirySource") == 20:
pass'base' return resource_custom_data[self.__class__.__name__][scenario][0].update(kwargs)
@abstractmethod def execute(self, params): return jsf_receive_enquiry_bill(data=json.dumps(params)
不同算法:

【python】class CreateTFCEnquiryBill(CreateEnquiryBillBaseAlgorithm): def read_params(self, **kwargs):
params = super().read_params(**kwargs) params[0].update({"businessCode": kwargs['businessCode'] if 'businessCode' in kwargs else f"TJ{laputa_util.date_time_str(fmt='%y%m%d')}{laputa_util.get_random_num(8)}","receiveBeginTime": tms_util.data_time_str(minutes=100),"deliveryBeginTime": tms_util.data_time_str(minutes=180)})
return params

def execute(self, params):
return super().execute(params)

class CreateECLPClodEnquiryBill(CreateEnquiryBillBaseAlgorithm):

def read_params(self, **kwargs):
# 若当前场景参数与基础参数改动较大建议直接在Yaml里另写Key
params=super().read_params(**kwargs)
params[0].update({"businessCode": kwargs['businessCode'] if 'businessCode' in kwargs else f"ECO{laputa_util.date_time_str(fmt='%y%m%d')}{laputa_util.get_random_num(8)}","receiveBeginTime": tms_util.data_time_str(minutes=100),"deliveryBeginTime": tms_util.data_time_str(minutes=180)})return params
def execute(self, params):
super().execute(params)return jsf_do_assign(data=json.dumps(params))
算法注入使用:

【python】
def receive_enquiry_bill(algOne=None, sceOne=None, **kwargs):
"""
Args:
algorithm: 业务类型
scenario: 测试场景:执行步骤,执行数据
Returns:
"""
if algorithm:
# 采用字典形式进行手动注册算法,由python动态查找
st = {"TFC": CreateTFCEnquiryBill(), "ECLP冷链": CreateECLPClod
EnquiryBill(), "TC": CreateTCEnquiryBill(),"终端用车": CreateTerminalEnquiryBill()}
query_algorithm = st.get(algOne)
return
query_algorithm.execute(query_algorithm.read_params(scenario=sceOne, **kwargs))
else:
pass
当有需求变动,只需修改其一策略规则内部代码,如【分单策略需求】,除运输内部系统TFC下发询价指定个体标签,其他上游没有增加标签下发功能,则只需修改CreateTFCEnquiryBill()代码即可。

5.2 Common用例组装

拼接task客户端方法组成case,利用feature组装测试数据,数据驱动测试方法执行。

【python】
@pytest.mark.parametrize("params", test_data('test_enquiry_core'), indirect=True)def test_enquiry_core(params):
enquiry_code = receive_enquiry_bill_core(**params).get("data")
return quote_enquiry_bill_core(enquiry_code=enquiry_code, **params)
06  总结

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。

随着运输八大产品建设方向逐步明确,自动化平台需要从应用维度重构到产品维度,在脚本不断融合和解耦过程,如何在新的分层模式设计高复用性脚本,需要大家结合各自业务条线不断优化改进。

有关高复用性自动化脚本设计实践的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  4. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  5. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  6. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  7. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  8. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  9. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  10. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

随机推荐