为什么我们使用 Docker 进行测试

2025-06-10

为什么我们使用 Docker 进行测试

或许,我从创建Agrippa至今学到的最重要的一课就是测试的重要性。当然,我之前就知道测试很重要——每个人都知道——但很容易就把它抛在一边,专注于更精彩的代码,或者编写一些敷衍的测试,实际上根本没法测试任何东西。然而,最终,在测试上懈怠会给自己带来麻烦;对我来说,幸运的是,在事情刚开始的时候就遇到了麻烦,但重点很明确——编写好的测试是重中之重。

具有挑战性的测试工具

然而,对于 Agrippa 来说,编写好的测试绝非易事——它是一个 CLI,用于根据项目环境(依赖项、配置文件的存在情况等)以及可选配置生成 React 组件.agripparc.json。换句话说,它的大部分工作是读取和解析命令行参数、查找和读取某些文件,最终结果是编写额外的文件。所有这些都不是纯粹的副作用,仅靠单元测试很难妥善覆盖。

此外,由于 Agrippa 的默认值很大程度上取决于项目环境,因此测试很容易因为存在不相关的文件或依赖项而返回错误结果。
最好用一个例子来解释这一点:运行时,Agrippa 会根据项目tsconfig.json中是否存在文件来自动检测项目是否使用 Typescript。但是,Agrippa 本身是用 Typescript 编写的,这意味着它的根目录中有一个tsconfig.json文件。因此,除非明确说明,否则每当在项目根目录的任何子目录中运行 Agrippa 时,它都会生成 Typescript ( .ts/.tsx ) 文件。并且,如果测试存储test在项目存储库中的文件夹中 - 它们都会被篡改(至少是那些查找文件的文件)。Agrippa 自己的 的存在也会导致类似的问题package.json

考虑到这一点,在规划测试实施时,我决定采用以下两个关键原则:

  1. 需要有良好的集成测试来测试该过程 - 包括其所有非纯粹效果(解析 CLI 选项、读取文件、写入文件) - 从开始到结束,在不同条件和不同环境下。
  2. 集成测试必须在尽可能隔离的空间中执行,因为该过程很大程度上依赖于其运行的环境。

第二点是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
Enter fullscreen mode Exit fullscreen mode

最终解决方案如下:
集成测试用例存储test/integrationAgrippa 存储库的 下。每个用例包含一个testinfo.json文件,该文件声明了有关测试的一些常规信息—— a name、 adescriptioncommand待运行的 ——以及一个目录solution,其中包含该命令将要创建的目录和文件。该test/integration目录还包含一个 Jest 配置 和integration.test.ts,其中包含测试逻辑本身。

Node 脚本运行时test:integration,会从位于项目根目录的 构建一个 Docker 镜像Dockerfile.integration。构建过程分为两个阶段:第一阶段复制项目源代码,构建并打包成 tarball 文件;第二阶段复制并安装该 tarball 文件,然后复制目录test/integration。构建镜像后,会从中创建一个容器,用于运行容器内的测试。

测试逻辑相当复杂。它会扫描test/integration目录中的案例,并为每个案例创建一个测试套件(使用describe.each())。每个案例的测试套件首先运行该案例——扫描solution目录,运行命令,然后扫描输出目录——然后比较两个结果。当(且仅当)拥有完全相同的目录、相同的文件,并且每个文件的内容相同时,agrippa该案例才被视为成功。solutionoutput

进一步改进

到目前为止,该解决方案运行良好。由于 Docker 需要设置时间,该脚本的运行时间比标准测试脚本更长(如果 Docker 需要构建镜像,则大约需要 60-70 秒,否则则需要几秒钟)。但是,它比实现自定义解决方案(例如使用临时目录)更简单、更健壮、更安全,并且添加新的测试用例也更容易,无需任何样板代码。

输出(为了显示目的而缩短)如下所示:
输出,缩短

该实现中有一个与 Docker 无关的问题,即使用 Jest 作为测试框架。事实证明,Jest 在异步测试方面存在局限性,并且组合动态数量的测试套件(每个案例一个),每个套件中动态数量的测试,以及在所有测试之前(扫描test/integration案例)和每个测试之前(运行案例)进行异步设置,根本行不通。

当我开始使用它时,我希望切换到不同的测试框架 - Mocha看起来很适合这种特定的场景,并且看起来很有趣。

结论

由于 Agrippa 对运行环境极其敏感,
我们需要完全隔离测试环境,才能确保测试的准确性。Docker 正好提供了这一点,因此我们选择了它。虽然 Docker 的解决方案需要一些时间才能正确实施,但效果很好。

你觉得怎么样?你有什么改进建议,或者想补充什么吗?我很乐意听取你的意见!
感谢你的宝贵时间。

鏂囩珷鏉ユ簮锛�https://dev.to/nitzanhen/why-we-used-docker-for-testing-p1d
PREV
从头开始学习 ASP.NET
NEXT
Agrippa 1.2 现已发布🎉🎉