您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
高复用性自动化脚本设计实践
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
高复用性自动化脚本设计实践
自猿其说Tech
2022-08-17
IP归属:未知
58440浏览
测试
### 1 痛点 随着运输业务场景的不断丰富和自动化脚本量的不断累积,日常在review用例时发现,目前大家仍停留在针对需求定制化用例编写,无法提高用例可复用性和可编排性。当业务流程中间某一环节发生变化时,不但需要重新修改脚本,还会影响当前应用其他用例执行结果。所以,如何设计高复用性脚本成为目前自动化建设的关键节点。 ### 2 设计理论 #### 2.1 设计理念 根据面向对象程序设计理念,设计者应遵循高内聚与低耦合原则,通常程序结构中各模块的内聚程度越高,模块间的耦合程度就越低。高内聚意味着一个类所能提供的功能应该是相关的,即一个类不要设计得包括很多互不相干的功能,低耦合代表要合理规划模块的颗粒度,即要保证一个模块可独立存在,降低模块之间复杂依赖关系。 #### 2.2 策略模式 策略模式定义了一系列的算法,将每一组相关的算法封装起各个策略分支,从而将分支相关的代码隐藏起来,并且使它们之间可以相互替换。策略模式让算法的变化不会影响到使用算法的客户,希望可以提高程序的可扩展性。 ### 3 解决思路 #### 3.1 基本思路 根据运输业务同一个流程存在不同场景,如询价服务接上游下发询价单节点,需要区分来源执行不同逻辑,目前设计五个算法能力,根据后期业务不断扩展,还会有更多算法加入进来,这个时候需要考虑一个好的结构对代码进行优化。可能前期大家通过if...elif...else 分支语句就可实现,但在考虑系统的健壮性和可维护性,这里就不能大量使用if分支语句。因为每一种算法能力的代码量极大且算法参数几十个,在随着更多上游接入可能存在十几个甚至更多else分支,很容易顾此失彼,牵一发而动全身。所以,利用策略模式设计一系列算法,再供用例拼装调用,提高代码的可读性和可复用性。 #### 3.2 方案分析 **优点** - 代码解耦,便于维护 - 避免使用难以维护的多重条件选择语句 - 可以运行时动态切换算法 - 开闭原则。无须对上下文代码进行修改,就可以添加新的代码 **缺点** - 如果算法逻辑,较为固定,不经常修改,使用策略模式只会增加代码量 - 必须知道所有的具体策略类及它们的区别 ### 4 方案概述 #### 4.1 分层改造 ![](//img1.jcloudcs.com/developer.jdcloud.com/d6456a13-ff02-41a2-a8ff-516ee2dda73420220817145209.png) #### 4.2 策略设计 ![](//img1.jcloudcs.com/developer.jdcloud.com/e67f1745-d437-42ae-a27c-f1f66a95e62c20220817145241.png) #### 4.3 操作步骤 - 将频繁修改的算法进行抽取,独立为具体的算法类 - 创建抽象基类,实现一个约定的抽象策略方法 - 所有独立的算法类,必须实现基类中的抽象策略接口 - 建立上下类,该类可以动态的对算法进行setter,创建调用具体算法的方法,上下文可通过该方法与具体的策略交互 - 客户端进行调用,传入具体的算法类,上下文动态执行具体的算法任务 ### 5 设计实践 #### 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 ``` **改造结构:** 上下文类 ``` 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) ``` 算法基类 ``` class CreateEnquiryBillBaseAlgorithm(ABC): # 算法能力基类 @abstractmethod def read_params(self, **kwargs): scenario=kwargs['scenario'] if "scenario" in kwargs and kwargs['scenario'] else '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)) ``` 不同算法 ``` 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)) ``` 算法注入使用 ``` def receive_enquiry_bill(algOne=None, sceOne=None, **kwargs): """ Args: algorithm: 业务类型 scenario: 测试场景:执行步骤,执行数据 Returns: """ if algorithm: # 采用字典形式进行手动注册算法,由python动态查找 st = {"TFC": CreateTFCEnquiryBill(), "ECLP冷链": CreateECLPClodEnquiryBill(), "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组装测试数据,数据驱动测试方法执行 ``` @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) ``` ### 6 总结 随着运输八大产品建设方向逐步明确,自动化平台需要从应用维度重构到产品维度,在脚本不断融合和解耦过程,如何在新的分层模式设计高复用性脚本,需要大家结合各自业务条线不断优化改进。 ------------ ###### 自猿其说Tech-JDL京东物流技术与数据智能部 ###### 作者:刘红妍
原创文章,需联系作者,授权转载
上一篇:拜占庭将军和 Raft 共识算法
下一篇:辅助测试和研发人员的一款小插件【数据安全】
相关文章
安全测试之探索windows游戏扫雷
Jmeter压测实战:Jmeter二次开发之JSF采样器实现
Laputa自动化测试框架介绍
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号