【测试工程思考】云平台测试可重用性和场景覆盖度的平衡术
1. 矛盾——What made us suffering
测试用例可重用性和场景多样化是矛盾的,在测试设计前期时,为了尽可能多的覆盖到现网存量场景,会在Xmind脑图中将测试输入/测试因子尽可能发散。不同测试输入下的调用链路径也会不同,导致测试用例完整的状态会显得十分臃肿和庞大。某些用例或测试活动可能只是针对现网一个局点的情况做验证。使得整个测试活动繁琐且低效。
随着系统演进,演化出来的场景越来越复杂,即使是一个很小的改动,也要考虑所有可能场景组合下的潜在风险。测试的工作量极具膨胀,时间有限,为了避免漏测,重点都投在了前期测试设计反串,头脑风暴和热烈的讨论后产出一个庞大的Xmind思维导图。后面就是加班加点的完成Xmind指导的动作,机械又重复,还要拉着各种开发定位问题,加班到头秃也没什么提升。
理论上的测试模型

实际的测试模型:

上述问题尤其在偏后端,偏基础设施的测试工程中,是一个典型的问题。分析和思考解决这个问题应该越早越好,而不是在用例膨胀到不得不动用工程手段时才解决。
2. 分析——Why we are suffering
笔者以云计算云平台框架服务为例,对测试遇到的困难进行分析和探索
2.1 基础设施差异
云平台框架对外屏蔽了节点的硬件差异,而针对新特性进行验证时,又不得不考虑硬件的兼容性,硬件差异是多维度的。
节点差异:K8S/openstack生态中,是控制节点和计算节点的差异,云计算语境下,还有网关节点、边缘节点等等节点角色上的差异。
网卡差异:尤其在云计算语境中体现明显。网络节点构筑了云计算厂商的关键网络能力。随着openstack-云原生-AI的发展路径,网卡形态也在一直演化,对于要考虑网络配置的云平台框架来说,也是不小的适配工作量。
组网差异:沿着openstack-云原生-AI的变革路径,云平台要提供规模更大的纳管能力、时延更短的网络传输,云平台因此会对底层组网重新设计。变革的过程中,现网必然会出现多种物理组网的场景,毕竟组网场景的收编涉及重大变更,实施需要联合各个服务验证后再转交付实施。整个过程需要很久。
2.2 版本差异
云平台要纳管10W量级的计算节点,单体的管理+控制集群已经无法满足要求了。势必会呈现出管理集群管理更小的计算集群的级联式架构。随着版本的迭代,一个region中的计算集群的server版本升级进度会参差不齐。又由于ECS服务或容器容器服务调度的细粒度,可能会出现一个计算集群内计算节点的agent版本进度也参差不齐。
这就出现了测试必须要考虑server-agent高低版本兼容性的问题。基于现网的版本分布画像,得出要验证的版本分布地图。这张地图上的路径数直接意味着理论上要全量验证的次数。
2.3 场景差异
在集成验证团队中,涉及到的场景有解决方案维度和服务域维度。
解决方案维度:按照整体解决方案实施建设的局点场景
服务域维度:服务域重点特性,可能还要考虑多个服务域关键特性的依赖。一个基础能力的变动就牵涉多个服务的可用性验证。
场景在多个维度上的演进为测试设计带来了更多不确定性,某个场景下的特性会逐渐演进到全网收编,某些特性有因为大的演进方向会被淘汰。
2.4 不同维度的差异导致的后果
因为上述差异,每个局点的场景特性都千姿百态。云平台的现网交付运维成了人肉运维,不做测试工程化规划的测试沦为人肉运维的打工人。走到这一步,具体的问题在特定的局点发生,除了老老实实构造测试场景,老老实实执行测试,写报告(没出现网问题还好),除了被动的负向改进(出了现网问题),测试工程师已经没有精力做更有价值的改变了。
3. 解决办法——How to make a change
3.1 约束场景发散
信仰演进式架构的架构师痴迷于加特性开关,在新需求串讲评审时,特性开关掩盖了特性上线实施时真正要面临的工作量和风险,也掩盖了构筑一个清晰明确的架构所需的深度思考。测试人员在参与评审时,可以这样去提问:
1. 规划这个特性开关的依赖特性有哪些,是否依赖某个服务、中间件的特定版本?
引导开发分析这个特性内部依赖的组件特性是否齐全、数据库表是否匹配、平台内部、第三方服务的版本兼容性是否要特殊考虑。
这些问题的答案,实际上是对后续测试执行工作量的剪枝:
1)明确依赖特性,在不涉及的特性场景下,不需要验证
2)明确依赖版本,在不涉及的版本路径上,不需要验证
3)明确影响功能,不需要验证不涉及的功能用例
一个对云平台演进历史熟悉的架构师应该对上述问题给出已经分析过的确定性结论,但为了避免临时的人员更替和人因差错,测试工程师要做double check。在这个阶段,一个有价值的问题可能识别出潜在的设计缺陷/遗漏,甚至直接改变特性开发的方向。
2. 这个特性开关是默认自动打开还是脚本打开还是变更手动打开?
从交付面运维风险的考量评估:默认自动打开 < 脚本打开 < 变更手动打开
因此这个提问的潜在动机是倒逼开发简化变更复杂度,能用脚本打开的,就不要涉及成变更手动打开,能在代码逻辑内自动打开的,就不要暴露到运维。这样研发可以通过版本升级加快一部分特性的收编,而不用再在研发环境上维护被关闭替换掉的特性
理想情况下,我们都希望特性的演进在研发内部闭环,越少涉及现网运维变更(越少产生变更SOP的验证工作量),越安全(基于测试充分验证的前提),越节省沟通成本,越少产生现网问题。
但现实往往要受制于有限的工期和人力。测试可以和开发基于当前的团队能力谈判。平衡两方团队的工作量。
3.2 梳理场景
演进到中后期的平台用例库庞大,每次版本验收,从中挑选继承用例时都令人头疼:
- 场景组合太多导致特性树杂乱,不能很快定位到关键特性用例
- 废弃、修改的特性没有及时更新,造成用例本身可用性差
在云平台服务演进到成熟阶段,用例重构应该是每个版本周期结束后需要投入的工作。解决上述两个关键问题,让用例成为真正有价值的测试资产。在偏重用例文本的测试资产和偏重用例脚本的测试资产在用例重构的工作上投入的成本收益比是明显有差异的。
在自动化基础能力建设完善的资产上,用例的重构显然要容易且有效得多。因此,引出下面的解决方法:
3.3 固化场景
测试脚本专注于特性的端到端验证,并不意味着要从无到有构造测试场景。场景应该固化为一层测试资源,在测试脚本中作为上下文引用。
例如验证各类网关节点上的LLDP配置是否生效,不同网关节点对应的网络配置模板不同,固化前的测试用例:
def test_lldp_on_ovs_dpdk(self):"""检查ovs-dpdk节点的lldp功能"""lldp_port = self.config_host_config_as_ovs_dpdk(hostid, ovs_dpdk)self.config_llpd_on_host(hostid)self.check_lldp_effect(hostid, lldp_port)def test_lldp_on_ovs_SDI(self):"""检查SDI节点的lldp功能"""lldp_port = self.config_host_config_as_SDI(hostid, ovs_dpdk)self.config_llpd_on_host(hostid)self.check_lldp_effect(hostid, lldp_port)
config_host_config_as_ovs_dpdk、config_host_config_as_SDI这类方法封装的是冗长的配置接口调用流程,往往不同的封装方法大部分代码是一样的,只有其中一两个接口的参数有差异。而且这个配置流程漫长,需要等待节点重启才能生效。端到端执行一个场景的用例就要10-20分钟。
固化主机网络配置模板:
固化配置,首先要固化对应的节点作为自动化测试场景对应的物料,不可用来扩容、减容、随意修改主机配置
def get_host_port_network_type(self, type):"""在测试物料中查找指定网络配置模板的节点"""for host, port in self.test_hosts:if host.network_type == type:return hostid, port
固化后的测试用例:
def test_lldp_on_host(self):"""检查lldp功能S1. 检查OVS-dpdk组网节点的lldp功能S2. 检查SDI节点的lldp功能......"""# S1hostid, lldp_port = self.get_host_port_network_type(type="OVS-dpdk")self.config_llpd_on_host(hostid)self.check_lldp_effect(hostid, lldp_port)# S2hostid, lldp_port = self.get_host_port_network_type(type="SDI")self.config_llpd_on_host(hostid)self.check_lldp_effect(hostid, lldp_port)
这样一来,冗长的端到端的,从无到有构造测试组网场景的封装方法就可以删除了。
此种固化方式实际上使用固定测试节点代替了大量重复的测试代码,用物料换取了测试可重用性和场景覆盖度的同时提升:
优势:更接近于现网的验证手段,用例可以平滑移植到生产环境拨测
劣势:增加了测试环境的建设成本,较为简单的测试环境无法覆盖全部场景
在具体实施时,我们可以评估全量脚本的固化策略,对强依赖硬件配置的用例,可以优先进行重构,因为本来物料成本代价就是等价的。在对一些纯配置/代码版本导致的场景差异进行合理规划,结合版本验证节奏,团队人员分工,将场景合理配置在不同的研发环境中。
