前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >持续测试的重要性

持续测试的重要性

作者头像
张逸
发布2023-03-23 18:22:37
4420
发布2023-03-23 18:22:37
举报
文章被收录于专栏:斑斓斑斓

几天前,我发表了文章《Twitter的问题说明再好的软件也会腐化》,文中提到避免软件腐化的三种有效手段,其中之一是持续测试

巧的是,上周刚拿到于君泽兄亲赠的译作《持续架构实践》。阅读到第二章,作者将持续测试作为架构演进过程反馈循环的重要实践。书中写道:

持续测试采用了左移方法,即通过自动化流程来显著提高测试速度,在各个开发阶段都确保质量达标。

书中给出该过程的示意图:

持续架构的本质就是为了在架构不断演进的过程中避免架构的腐化,而避免架构腐化的前提则需要迅速而及时地知道架构可能走向腐化的征兆。通过建立评估架构的指标,尤其是评估架构质量属性的指标,然后利用持续测试建立相应指标的测试用例,将其融入到CI/CD流水线中,让每次代码变更的提交都必须通过指标的验证,如此就建立了对架构质量的反馈循环。

以下是我在多个软件项目中对持续测试的实践。

01

性能测试围栏

我们为北美一家客户开发医疗内容管理系统。在该项目的先启(inception)阶段,通过trade off slider了解到客户的关注点,如下图所示:

位于图左侧的表示可以协商(most negotiable),位于图右侧则表明无可商量(least negotiable)。显然,除了交付日期之外,客户最关注的就是可用性与性能。

为了响应客户的关注,我们从项目开初就在CI流水线增加了专门的性能测试,并根据客户的质量属性定义,确认对应的性能测试用例,并明确响应时间的具体值作为测试指标,将其设定为CI的阈值。每次提交代码,都会触发性能测试,一旦响应时间超过我们设置的阈值,CI就会变红,提交者就必须定位导致性能下降的原因,并及时修复。

针对与性能有关的需求编写用户故事时,验收标准需明确给出性能指标,并要求团队成员在完成该用户故事时,必须编写对应的性能测试。

虽然这一严格的要求给我们的团队制造了不少“麻烦”,让我们在提交代码时,总是战战兢兢,仿佛性能测试像一头猛兽,一不小心就会被变更提交放出来,然后残忍地把提交者吃掉。没错,每次破坏了性能测试,提交者面临的痛苦不亚于被猛兽咬啮,却让真正的灾难变得可控。

我们给自己打造了围栏,目的是确保我们可以自始至终保证前进的方向。而收获非常可观,我们一直确保了客户的关注得到重视和保障,提升了交付满意度。

02

验收测试保护网

在为澳大利亚一家金融客户超过8年历史的遗留项目开展技术栈迁移时,客户付出了极大成本要求团队建立覆盖几乎所有场景的验收测试保护网。

我们建立了专门的自动化测试团队,在当时,选择JBehave作为编写验收测试的工具。编写自动化测试的过程,也是获取知识的过程。要知道所谓“遗留系统”,实则就是缺少知识的软件系统

软件系统知识的三个载体如下图所示:

对于一个历史超过八年的软件系统,参与过项目开发的团队成员已经十不留一,至于文档,到底有多少内容是系统的真实面貌呢?剩下的就是最为真实的代码,然而庞大而糟糕的代码库,仿佛迷雾重重的冒险乐园,需要我们不断探索。

我们以BDD(Behavior-Driven Development)的规格(Specification)格式编写验收测试,如下图所示:

这一形式的验收测试完全是对真实业务场景的重现,而它们又都是可运行的,从而形成了整个软件项目的活文档。比之于厚厚一叠超过400多页如圣经一般的需求规格说明书,这些验收测试更加真实,也更加准确。

运行这些测试也能帮助我们发现已有代码与文档的不一致,也可以帮助我们更好地探测源代码在运行时的调用关系,尤其针对消息驱动的分布式系统,价值更为明显。

在搭建好由这些验收测试组成的保护网后,就可以相对安全地开展技术栈迁移了。通过CI获取快速反馈,确定每次迁移是否破坏已有功能。我们还引入feature toggle(特性开关),在保留旧有调用的同时,增加迁移后的新功能,然后配置特性开关来决定到底调用旧还是新的实现。

03

包的依赖检查

在为中国香港一家客户提供架构评审咨询时,我引入了ArchUnit工具,它支持我们以编写单元测试的方式对系统的架构指标——包的依赖关系——进行检查:

使用该工具非常方便,直接在maven加入它的依赖,之后就可以像编写单元测试那样调用ArchUnit的API对包的依赖关系进行检查。例如,假设不允许source包依赖foo包,如下图所示:

则可以在单元测试中编写如下代码:

代码语言:javascript
复制
noClasses().that().resideInAPackage("..source..")
    .should().dependOnClassesThat()
    .resideInAPackage("..foo..")

又例如允许source.one包依赖foo包,但禁止source.two和source.any对foo产生依赖,如下图所示:

则可以编写:

代码语言:javascript
复制
classes().that().resideInAPackage("..foo..")
    .should().onlyHaveDependentClassesThat()
    .resideInAnyPackage("..source.one..", "..foo..")

如下代码则是我为客户编写的验证分层调用关系的单元测试:

代码语言:javascript
复制
@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "hk.org.xx.model.xxx.xxxx")
public class LayerArchitectureTest {
    @ArchTest
    public static final ArchRule layer_dependencies_are_respected = layeredArchitecture()
            .layer("biz").definedBy("hk.org.xx.model.xxx.xxxx.biz..")
            .layer("persistence").definedBy("hk.org.ha.model.cms.hafm.persistence..")
            .whereLayer("biz").mayNotBeAccessedByAnyLayer()
            .whereLayer("persistence").mayOnlyBeAccessedByLayers("biz");
}

是否非常容易呢?

架构腐化是我们需要不断斗争的敌人,而持续测试会是一件非常锋利的武器。不要把它藏到武器库,赶紧用起来吧!


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 逸言 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01
  • 性能测试围栏
  • 02
  • 验收测试保护网
  • 03
  • 包的依赖检查
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档