为什么我们使用 Docker 进行测试
或许,我从创建Agrippa至今学到的最重要的一课就是测试的重要性。当然,我之前就知道测试很重要——每个人都知道——但很容易就把它抛在一边,专注于更精彩的代码,或者编写一些敷衍的测试,实际上根本没法测试任何东西。然而,最终,在测试上懈怠会给自己带来麻烦;对我来说,幸运的是,在事情刚开始的时候就遇到了麻烦,但重点很明确——编写好的测试是重中之重。
具有挑战性的测试工具
然而,对于 Agrippa 来说,编写好的测试绝非易事——它是一个 CLI,用于根据项目环境(依赖项、配置文件的存在情况等)以及可选配置生成 React 组件.agripparc.json
。换句话说,它的大部分工作是读取和解析命令行参数、查找和读取某些文件,最终结果是编写额外的文件。所有这些都不是纯粹的副作用,仅靠单元测试很难妥善覆盖。
此外,由于 Agrippa 的默认值很大程度上取决于项目环境,因此测试很容易因为存在不相关的文件或依赖项而返回错误结果。
最好用一个例子来解释这一点:运行时,Agrippa 会根据项目tsconfig.json
中是否存在文件来自动检测项目是否使用 Typescript。但是,Agrippa 本身是用 Typescript 编写的,这意味着它的根目录中有一个tsconfig.json
文件。因此,除非明确说明,否则每当在项目根目录的任何子目录中运行 Agrippa 时,它都会生成 Typescript ( .ts
/.tsx
) 文件。并且,如果测试存储test
在项目存储库中的文件夹中 - 它们都会被篡改(至少是那些查找文件的文件)。Agrippa 自己的 的存在也会导致类似的问题package.json
。
考虑到这一点,在规划测试实施时,我决定采用以下两个关键原则:
- 需要有良好的集成测试来测试该过程 - 包括其所有非纯粹效果(解析 CLI 选项、读取文件、写入文件) - 从开始到结束,在不同条件和不同环境下。
- 集成测试必须在尽可能隔离的空间中执行,因为该过程很大程度上依赖于其运行的环境。
第二点是Docker的必要性——最初,我尝试在 Node 创建的临时目录中实现测试,并在其中运行测试,但事实证明,构建和维护这样的目录工作量太大,而且理论上创建的目录可能不够纯净。
而 Docker 则专注于轻松搭建隔离环境——我们可以完全控制操作系统、文件结构和现有文件,并且对所有这些都更加透明。
在我们的例子中,在 Docker 容器中运行测试可以实现所需的隔离。所以我们选择了:
解决方案
# Solution file structure (simplified)
test/integration/
├─ case1/
│ ├─ solution/
│ │ ├─ ComponentOne.tsx
│ │ ├─ component-one.css
│ ├─ testinfo.json
├─ case2/
│ ├─ solution/
│ │ ├─ ComponentTwo.tsx
│ │ ├─ component-two.css
│ ├─ testinfo.json
├─ case3/
│ ├─ ...
├─ integration.test.ts
├─ jest.integration.config.js
Dockerfile.integration
最终解决方案如下:
集成测试用例存储test/integration
在Agrippa 存储库的 下。每个用例包含一个testinfo.json
文件,该文件声明了有关测试的一些常规信息—— a name
、 adescription
和command
待运行的 ——以及一个目录solution
,其中包含该命令将要创建的目录和文件。该test/integration
目录还包含一个 Jest 配置 和integration.test.ts
,其中包含测试逻辑本身。
Node 脚本运行时test:integration
,会从位于项目根目录的 构建一个 Docker 镜像Dockerfile.integration
。构建过程分为两个阶段:第一阶段复制项目源代码,构建并打包成 tarball 文件;第二阶段复制并安装该 tarball 文件,然后复制目录test/integration
。构建镜像后,会从中创建一个容器,用于运行容器内的测试。
测试逻辑也相当复杂。它会扫描test/integration
目录中的案例,并为每个案例创建一个测试套件(使用describe.each()
)。每个案例的测试套件首先运行该案例——扫描solution
目录,运行命令,然后扫描输出目录——然后比较两个结果。当(且仅当)和拥有完全相同的目录、相同的文件,并且每个文件的内容相同时,agrippa
该案例才被视为成功。solution
output
进一步改进
到目前为止,该解决方案运行良好。由于 Docker 需要设置时间,该脚本的运行时间比标准测试脚本更长(如果 Docker 需要构建镜像,则大约需要 60-70 秒,否则则需要几秒钟)。但是,它比实现自定义解决方案(例如使用临时目录)更简单、更健壮、更安全,并且添加新的测试用例也更容易,无需任何样板代码。
该实现中有一个与 Docker 无关的问题,即使用 Jest 作为测试框架。事实证明,Jest 在异步测试方面存在局限性,并且组合动态数量的测试套件(每个案例一个),每个套件中动态数量的测试,以及在所有测试之前(扫描test/integration
案例)和每个测试之前(运行案例)进行异步设置,根本行不通。
当我开始使用它时,我希望切换到不同的测试框架 - Mocha看起来很适合这种特定的场景,并且看起来很有趣。
结论
由于 Agrippa 对运行环境极其敏感,
我们需要完全隔离测试环境,才能确保测试的准确性。Docker 正好提供了这一点,因此我们选择了它。虽然 Docker 的解决方案需要一些时间才能正确实施,但效果很好。
你觉得怎么样?你有什么改进建议,或者想补充什么吗?我很乐意听取你的意见!
感谢你的宝贵时间。