引擎盖下:玩笑如何找到相关测试
#javascript #算法 #jest #unittest

在我以前的articles中,我已经谈论过有时我们甚至没有注意到我们周围的算法。当使用工具或库时,我们将其作为给定食用,甚至不理解幕后的工作方式。今天,我将逆向工程算法开玩笑来查找相关测试时,我们运行jest --findRelatedTests

什么是玩笑,我如何使用它?

Jest是当今几乎每个项目中使用的流行JavaScript测试框架。您可以简单地通过使用test.js扩展名创建任何测试文件并在您的终端中运行嘲笑命令开始。 JEST还提供了广泛的配置选项开发人员可以用来定义要包括/排除的测试,要生成的覆盖范围输出,以哪种格式,如何移动源文件等。-您可以在official docs中找到所有这些。最令人兴奋的选项是用--findRelatedTests和要测试的文件的传递列表来运行开玩笑:

jest --findRelatedTests /path/to/file1, /path/to/file2

这种方式开玩笑将不仅运行与这些文件关联的直接测试,还将运行对传递依赖性的测试。优化GIT预命令命令执行时,它变得非常方便。开玩笑只会运行一组测试,而不是每当您要进行新的更改时,就不会运行一组测试,而是要运行一组测试,这些测试实际上会受到相应的更改节省开发人员的影响,同时准确地与结果进行准确。

有一个文件系统

玩笑使用自定义的haste module system以优化的方式构建有关文件和依赖关系的元数据。这个组成部分绝对值得在《胡德》文章下得到自己的。就目前而言,让我们简化它提供了API,以以下形式获取项目中的所有文件及其依赖项的集合:

Array<{ 
// absolute file path 
file: string; 

// list of depedencies (modules imported in current file)
dependencies: Array<string>; 
}>

看着引擎盖

我们知道什么:

  • 已在git
  • 中添加/修改/删除并上演的一组文件
  • 和整个文件系统元数据

现在,我们需要找到一种算法来检测并运行仅受此更改影响的测试文件。

和Jest提供resolveInverse API来解决此问题:

resolveInverse(
paths: Set<string>, 
filter: (file: string) => boolean, 
options?: ResolveModuleConfig, ): Array<string> { 
   return this.resolveInverseModuleMap(paths, filter, options)
          .map( module => module.file, ); 
}

其中:

  • paths是一组已更改的文件路径
  • filter是一个谓词function,用于检测当前文件是否是测试文件
  • options对象(与算法无关,暂时忽略它)

resolveInverseModuleMap使用一种非常简单的方法来解决基于Breadth First Search(BFS)的问题。首先,它初始化以下集:

  • changed-受影响的一组文件。基本上,这是一个临时集,在每次BFS的每次迭代后都会突变,并用作终止指示器 - 算法在空为空时会停止。在第一次迭代中,该集合由提供的paths填充。
  • relatedPaths- test 文件的集合。默认情况下,该集合是从paths输入中填充的,但条件是该文件实际上是一个测试文件。在BFS的每次迭代中,每个匹配的测试文件都从此文件中删除。在算法的末尾,该集合中的剩余文件路径与BFS执行的结果相连。这是在测试文件中而不是在源中进行更改时处理简单的用例。
  • visitedModules-已经被遍历并可以从进一步处理中跳过的文件。

所以让我们可视化我们的算法。首先,让我们定义我们的示例文件系统:

红色表示的块表示更改的模块(矩形是测试文件,而圆圈是源文件)。箭头从父到子开始(这意味着a模块由a.testa1导入)。最初,我们的数据结构看起来像这样:

我们的changed套件不是空的。这意味着我们需要遍历文件系统中的所有文件,以获取changed集中每个模块的依赖项列表。在第一次迭代之后,我们已经发现至少b.testa1.test需要分别作为a.testa1的依赖项运行:

做另一个遍历。现在,我们看到b2已导入a.testb2.test,但是我们在最初更改的文件中已经有a.test。在这种情况下,我们需要将其从relatedPaths集中删除,并将其添加到结果中。如果a.test没有任何依赖性,JEST将其返回到上游呼叫者时简单地将结果合并为结果。

最后,我们在changed集中只有2个测试模块,并且已经看到a.test在我们访问的集合中,而b2.test没有任何依赖人。基本上,这里的no -op-此迭代后,更改集将变为空,我们将脱离BFS循环。

结果,我们可以将测试范围从5个测试文件缩小到4。这并不令人印象深刻,但请记住,我为简单起见选择了一个很小的数据集,在现实世界项目中,将会有数千个文件,并且改进确实可以很大。

您可以找到原始实现[此处] https://github.com/facebook/jest/blob/e865fbd66e3dc4adf9d35a35ce91de1bee48bc93/packages/jest-resolve-dependencies/src/index.ts#L99)_._

最后的想法

您认为该算法可以改善吗?如果我们可以作为文件元数据的一部分(类似于对父母的引用的DOM元素)具有与因模块的逆链接,该怎么办?在这种情况下,我们不必遍历整个文件系统,而只专注于一个子集。

最初于2022年12月22日在 https://thesametech.com 发表。

您也可以 follow me on Twitter subscribe to my Telegram channel connect on LinkedIn 以获取有关新帖子的通知!