雷锋网 AI 科技评论按:如何减轻软件开发的回测压力,从而提高工程师的生产效率?MATEUSZ MACHALICA、ALEX SAMYLKIN 等人组成的 Facebook 研究团队提出使用一个利用机器学习的新系统来创建一个为特定代码更改选择回归测试的概率模型,从而更好地执行这种回归测试。
为了高效地开发新产品特征和更新,Facebook 研究团队使用基于主干的开发模型来管理对代码库的改动。一旦一位工程师的代码更改被接入主分支(主干),他们试图让它对从事该产品或服务的其他工程师快速可见。这种基于主干的开发模型比使用特征分支和特征融合更加有效,因为它使得每个人都能够在代码库的最新版本上工作。
但是,在被接受到主干之前,对每项提出的更改进行彻底的回归测试很重要(注:回归测试是指修改了旧代码后, 重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误的一种测试方法)。在从主干被部署到生产之前,每项代码更改都需要经过彻底的回归测试,进入主干异常代码会使得评估新提出的代码更改变得更困难得多,并且还会影响工程师的生产效率。
对此,该研究团队开发了一种更好的方法来执行这项回归测试:使用一个利用机器学习的新系统来创建一个为特定代码更改选择回归测试的概率模型。这种方法需要仅仅运行一个小的测试集,以确保检测到错误的更改。与典型的回归测试选择(RTS)工具不同,该系统通过从历史代码更改和测试结果的大型数据集中学习,来自动开发测试选择策略。
这个预测性测试选择系统已在 Facebook 上部署了一年多,在一段新的代码加入到主干、被其它工程师看到之前,这个系统就可以捕捉超过 99.9% 的回归异常,而且它运行的基于修改的代码的测试数量也只需要以往的三分之一那么多。这也让 Facebook 的基础测试设施的效率得到翻倍的提升。
随着代码库的不断发展,该系统也几乎不要求手动调试。而且经证明,它还能够捕捉产生不一致和不确定性结果的片状测试。
回归测试的一种常用方法,就是使用从构建元数据中提取的信息来确定在特定代码更改上运行哪些测试。通过分析代码单元间的创建依赖项,可以确定传递依赖于在代码更改中被修正的源的所有测试。例如,在下图中,圆圈表示测试;正方形表示代码的中间单元,如库;菱形表示存储库中的单个源文件。箭头连接起实体 A →B,当且仅当 B 直接依赖于 A 时,他们将其解释为 A 影响 B。蓝色的菱形表示在示例代码更改中被修正的两个文件,所有传递依赖于它们的实体也用蓝色表示。在这个场景中,基于创建依赖项的测试选择策略将执行测试 1,2,3 和 4,但不执行测试 5 和 6,因为后两项测试不依赖于修正的文件。
这种方法有一个明显的缺点:它以说「是的,本测试受到影响」告终的次数比实际所需要的要多。平均而言,对于移动代码库的每项更改,该方法都会导致执行多达四分之一的可用测试。如果传递依赖于修正文件的所有测试都真正受到影响,他们将别无选择,而只能将每项测试都执行一遍。然而,在他们的单片代码库中,终端产品依赖于许多可重复使用的组件,这些组件使用一小组低级库。在实践中,许多传递性依赖实际上与回归测试无关。例如,当某个低级库发生更改时,在使用该库的每个项目上重新运行所有测试将是低效的。
软件开发研究领域也开发了其他的回归测试选择方法,例如基于静态更改-影响分析的方法。然而,由于他们代码库的大小和使用的不同编程语言的数量,这些技术在他们的使用案例中是不现实的。
基于创建依赖项的选择测试涉及到判断哪些测试可能受到更改的影响的问题。为了开发更好的方法,Facebook 的研究团队考虑了一个不一样的问题:指定的一项测试发现某个代码修改中的回归问题的可能性有多大?如果他们能估计到这个可能性,就可以做出明智的决定,来排除那些极不可能发现回归的测试。这是对传统测试选择的重大背离,并且开辟了一种新的、更有效的选择测试方法。
作为第一步,该研究团队创建了一个预测模型,该模型针对新提出的代码更改估计每项测试失败的概率。他们通过使用包括历史代码更改上的测试结果在内的大型数据集,然后采用标准的机器学习技术来创建模型,而非手动定义模型。
每个新的代码更改总会与之前的情况略有不同,因此模型不能简单地将新的更改与历史更改进行比较,来确定哪些测试值得运行。然而,新更改的抽象可以类似于前一个或多个代码更改的对应的抽象。
在训练期间,研究团队的系统学习基于源自先前代码更改和测试的特征的模型。然后,当该系统正在分析新的代码更改时,他们将学习到的模型应用于基于特征的代码更改的抽象。对于任何特定的测试,该模型接着能够预测检测到回归的可能性。
为此,该系统使用了标准机器学习算法的变体——梯度提升决策树模型。研究团队虽然可以使用其他机器学习算法,但其之所以选择这种方法,有几个原因:决策树是可解释的、易于训练的,并且已经是 Facebook 机器学习算法基础结构的一部分。
他们可以使用这个模型分析特定的代码更改,来找到所有传递依赖于修改文件的可能受影响的测试,然后估计测试检测到由更改引入的回归的概率。基于这些估计,系统选择对于特定更改最有可能失败的测试。下图显示了将选择哪些测试(用蓝色表示),来更改影响前一示例中的两个文件,而在前一示例中,用 0 到 1 之间的数字来表示每个被考虑在内的测试的概率。
对于每项代码更改,系统选择的测试数量影响它在检测回归时的可靠性。使用最近代码更改的选择作为验证集,研究团队可以评估其在新更改上的准确性。下面的图表显示了每次更改所选择的最大测试数量与这一选择的准确性之间的关系。在生产中,他们要求其模型能够正确预测超过 95% 的测试结果,并且能为超过 99.9% 的有问题的更改捕获至少一个失败的测试。他们发现,这种准确度的高标准所带来的测试信号的损失可以忽略不计,并且消除了大量不必要的测试执行。
由于代码库结构的不断演变,测试选择策略必须适应继续满足这些严格的正确性要求。然而,他们的系统让其变得简单,因为他们可以使用最近提交的代码更改的测试结果来定期地重新训练模型。
为了确保他们的测试选择很好地适用于现实世界的测试,系统需要处理测试片状问题:当被测试的代码没有真正被更改时,测试结果从通过变为失败。正如他们在论文中所做的更详细的解释,如果他们训练一个模型而不去识别片状测试失败,该模型可能无法学习去一致地预测测试结果。在下面的示例中,两个测试选择策略捕获所有失败的测试执行的共同部分。如果系统不能区分哪些测试失败是片状的以及哪些不是,那么它将无法知道哪个策略是最好的。策略 A 具有明显更好的准确性,因为它捕获了所有无法发现实际回归的测试。然而,策略 B 选择了大量由于片状性而非代码的实际问题而失败的测试。
为了减轻片状性对所学到的测试选择模型的影响,研究团队在收集训练数据时积极地重新尝试失败的测试。这种方法让他们将连续失败的测试(指示真实回归)与那些呈现片状、非重现性失败的测试区分开来。
这个系统是研究团队创建智能工具以使代码开发过程更加可靠和高效的更广泛努力的一部分。他们的基于搜索的自动化软件测试系统 Sapienz 和自动化缺陷修复工具 Getafix,也可以帮助他们自动检测和修复回归——也就是说,这些工作仅要求工程师们投入很少的注意力甚至不投入注意力。
预测性测试选择(这篇博客文章中描述的系统)通过选择由工程师定义的正确的测试集,来高效地检测回归。Sapienz 生成新的测试序列,来发掘让移动应用程序崩溃的条件,Getafix 则为他们使用测试和验证工具所发现的问题推荐补丁,然后由编写更改的工程师检验并选择接受或拒绝这些补丁。总而言之,这些系统让工程师能够为使用 Facebook 产品的数十亿人,更快、更有效地创建和部署新特征。
预测性测试选择是 Facebook 的数个项目中的一个,它旨在应用统计学方法和机器学习来提高回归测试的有效性。随着研究团队进一步提高系统的效率和准确性,他们也将应用相关的方法来识别测试范围中的潜在差距。
机器学习正在变革生活的方方面面。他们相信软件工程在这方面也一样。
via:Predictive test selection: A more efficient way to ensure reliability of code changes,https://code.fb.com/developer-tools/predictive-test-selection/ 雷锋网 AI 科技评论编译。雷锋网