当前位置: 首页 > 科技观察

您可能不需要的两个很酷的Java框架

时间:2023-03-15 20:54:06 科技观察

我们都参加过-甚至可能发表过-演讲者特别迷恋某种语言或工具,并且在诸如“只需添加此配置键或依赖性”经常用在像这样的短语中。总是建议一些健康的怀疑主义来抵消这种热情,特别是当技术是新的、很少使用的、专门的或这三者的结合时。软件框架永远不会免费工作,即使你不要支付许可费。不熟悉的技术有一个学习曲线,无论多么温和。不要告诉我只要在build.gradle中添加“仅”一行(或在pom中添加20行)就可以获得工具X的所有好处...'雇了一个是个艺术家。这样的方程不可能用硬数字来表达。常识必须足够。基于属性的测试和变异测试在本文中,我想讨论Java测试工具包中两种成熟但相对小众的技术:使用Pitest的变异测试和使用Jqwik的基于属性的测试。我以前写过关于PBT和MT的文章,带着开发人员的帽子和“为技术而技术”的心态。现在,我将戴上CFO的预算批准帽,并解释为什么您在使用它们之前应该三思而后行。但首先,如果您是这些技术的新手,请简单回顾一下。突变测试(MT)框架对被测编译代码进行微小但重要的更改(突变)。别担心,这是在内存中完成的,无需触及源代码。JVM仍然可以运行新的字节码,但是如果您有足够的覆盖率,更改后的行为现在应该会导致至少一个单元测试失败。用玩家的话说,我们称之为杀死突变体。当测试套件具有高覆盖率但断言不佳时,MT尤其具有指导意义。许多测试将保持绿色,但MT保持绿色是个坏消息。基于属性的测试(PBT)则完全不同。它允许您为属性定义测试场景,这些场景是适用于一系列值的真实语句。上面写着“18岁以下禁止入内”。使用PBTbooleanisAgeAllowed(intage)可以使用0到18之间的随机值范围调用一些方法。有关示例,请参见配套的GitLab项目中的AdmissionCalculatorPropertySuite。MT是一种质量验证技术,可以发现现有测试中缺失或不完整的断言。PBT通过从多个角度进行测试来增强现有测试。它们位于单元/组件/集成/端到端测试金字塔的底部。尽管它们存在差异,但它们有一个重要的共同点:它们是可以使结构良好的代码库变得更好的工具,但在测试成熟度较低的项目中,它们即使不是真正有害的,也是无用的。此外,它们对于有效部署和使用也很重要。让我们用一个好的测试驱动方法的典型例子来说明。您正在编写一个根据赞助人的出生日期计算入场费的组件。幸运的是,您的团队非常规范:一个人的年龄必须评估为非负值。带有“无效出生日期”的异常信号。4岁以下的儿童或90岁以上的成人不得参加这个可怕的主题公园游乐设施。例外情况是“客户必须在4到90岁之间”。入场费为15岁及以下10欧元,16岁及以上15欧元。该代码是一组简单的整数值if语句(请原谅我的冗长)。完整代码在这里。Java1if(age<0){2thrownewIllegalArgumentException(“出生日期[%s]无效”。已格式化(dateOfBirth));3}elseif(age<4||age>90){4thrownewIllegalArgumentException("Customermustbebetween4and90yearsold,butis[%s]".formatted(age));5}elseif(age<16){6返回10;7}否则{8返回15;9}使用像这样的简单代码可以轻松实现100%坚如磐石的覆盖率。请参阅入学计算器套件。对业务规则的大多数更改都会自动导致测试失败,但不是全部。让我们介绍一个新规则。65岁或以上的成年人支付10欧元。因此,儿童和老年人有资格享受折扣。在代码方面:if(age<16)变为if(age<16||age>=65)并且您的所有单元测试仍然通过。类、方法、行甚至分支覆盖率仍然是100%。测试说出了真相,但它不再是全部真相,因为在值65附近引入了一个新的、未经测试的边缘案例。如果你的工作是测试驱动的,那么你应该在添加新条件之前编写额外的测试场景。当突变测试无济于事时,机器翻译能否发现这一遗漏?是的,它可能已将age>=65更改为age>65并提醒您没有测试可以覆盖这种边缘情况。但是您可以而且应该在实施更改时意识到这一点。您可以正确地争辩说,生产代码从来没有像这个例子那样微不足道。当您继承大型代码库时,MT肯定更有利于提高测试质量吗?这就是为什么我可能不是。当您的测试套件的断言很差时,您不需要MT来告诉您。在src/test/java中对“assert”进行全文搜索将告诉您需要知道的一切。您不需要MT来检查测试覆盖率。有更有效的工具可以做到这一点。如果找到大部分代码,首先,MT无法产生任何有用的东西,因为没有杀死突变体的测试代码。像Pitest这样的工具可以生成非常精确但也很详细的关于未被发现的突变体的报告。如果你有很好的覆盖率但断言很差,这是巨大的。这就像一个747驾驶舱,所有警告灯同时闪烁。知道首先解决哪个问题需要判断。杀死所有突变体没有意义,因为100%的测试覆盖率通常是不值得的。MT框架为每个突变体多次运行相同的测试场景,因此被测代码应该快速执行。访问数据库、文件系统或网络会使变异测试运行缓慢得令人无法接受。同样,具有长方法和高圈复杂度的非内聚代码创造了许多引入突变的机会。相同的长方法将被调用无限次。使用PBT捕获未知边缘情况PBT可以很好地捕获业务逻辑中未经测试的添加。单元测试告诉你真相,但属性测试告诉你全部真相。由于它会验证4到90之间的所有值,因此当它达到65到90范围时它现在将失败。java1@property2publicvoidany_age_between_four_and_ninety_is_valid(@ForAll@IntRange(min=4,max=90)intage){3assert(getAdmissionForAge(年龄))。是积极的();4}从表面上看,上面的代码看起来像参数化测试Java1@ParameterizedTest2@ValueSource(ints={4,90})3publicvoidany_age_between_four_and_ninety_is_valid(integerage){4...}5不要被愚弄了,虽然。上面的单元测试不测试“任何年龄”,只测试我们碰巧知道的边缘情况。使用PBT作为解决您忘记的边缘情况的猎枪方法很诱人,但这违背了它的精神。您应该从记录的属性开始并将它们变成可运行的测试用例。指定这些属性应该先于测试和生产代码。没有框架可以挽救不合标准的测试当测试成熟度较差的团队编写单元测试时,通常是为了确认生产代码的作用。给定值X和Y,被测方法返回Z,这就是我们所断言的。用一千个不同的值来打它(就像PBT那样)似乎毫无意义。如果你的自动化测试只是在强化这样的现状,那真的毫无意义。您可以从这种方法中得到的最好的东西是一些防止回归的保护。PBT和MT都帮不了你。它们无法揭示实施中的逻辑失误,更不用说解释设计中的错误了。一开始可能不会。MT和PBT在经过良好测试的关键业务代码中具有价值,这些代码充满了if语句、开关和(数字)边缘情况,您需要金钱可以买到的所有稳健性。相反,如果一个方法对于任何浮点值的行为都是可预测的,那么用随机输入对其进行一千次测试不会给你太多的洞察力。不要将这些框架用于支持功能,即支持应用程序算法核心的代码:Web或消息传递端点、数据库访问层、安全层或数据传输映射逻辑。不要编写遇到此类代码的PBT场景,并确保Pitest忽略这些部分进行突变。这些资源密集型框架只有在您将重要算法适当地隔离成小类时才值得,这些小类可以在不破坏房屋的情况下进行数千次测试。当您将逻辑重构为可测试的块并提高单元测试的覆盖率和质量时,您甚至可能会发现不再需要变异测试。PBT和MT是令人着迷的技术,所以一定要检查一下。但它们也是硕士论文的内容。他们对他们有一点学术的感觉,在质量承担成本并且必须可以协商的商业世界之外。如果您决定使用它们,请花时间好好理解它们,不要陷入为了测试而测试的心态。