使用 Docker 进行 JavaScript 集成测试:从理论到高级实践
\n\n集成测试是开发中至关重要的阶段,它检查不同模块与外部服务之间的交互。对于基于 Node.js 的 JavaScript 应用程序,Docker 成为不可或缺的工具,它能够创建隔离、可重现的测试环境。在本文中,我们将探讨使用 Docker 配置集成测试的高级技术,包括与数据库、队列的协作以及针对 CI/CD 的优化。
\n\n为什么在集成测试中使用 Docker?
\n\n传统的集成测试方法(mocks, stubs)并不总能反映系统的真实行为。Docker 通过提供以下功能解决了这个问题:
\n\n- \n
- 隔离性:每个测试在干净的容器中运行,不会影响其他测试或本地环境。 \n
- 可重现性:在开发机器和 CI/CD 中具有相同的执行条件。 \n
- 可扩展性:能够并行运行具有不同依赖版本(PostgreSQL, Redis, Elasticsearch)的测试。 \n
- 快速恢复:重新创建容器只需几秒钟,而不是数小时的配置时间。 \n
配置高级测试环境
\n\n考虑一个典型的 Node.js 应用程序,它使用 PostgreSQL 和 Redis。我们将创建一个灵活的配置,支持并行测试和健康检查,而不是简单的 docker-compose.yml。
步骤 1:使用动态端口的 Docker Compose
\n\n为了避免并行运行时出现端口冲突,我们使用动态分配:
\n\n# docker-compose.test.yml\nversion: '3.8'\nservices:\n postgres_test:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: test_db\n POSTGRES_USER: test_user\n POSTGRES_PASSWORD: test_pass\n ports:\n - "0:5432" # 动态端口\n healthcheck:\n test: ["CMD-SHELL", "pg_isready -U test_user -d test_db"]\n interval: 2s\n timeout: 5s\n retries: 10\n tmpfs: /var/lib/postgresql/data # 数据存储在 RAM 中以加快速度\n\n redis_test:\n image: redis:7-alpine\n ports:\n - "0:6379"\n healthcheck:\n test: ["CMD", "redis-cli", "ping"]\n interval: 2s\n timeout: 5s\n retries: 10\n\n步骤 2:用于 Docker 测试的实用工具
\n\n创建一个辅助类,自动查找动态端口并等待服务就绪:
\n\n// test/setup/docker-manager.js\nconst { DockerComposeEnvironment, Wait } = require('testcontainers');\nconst { Client } = require('pg');\nconst Redis = require('ioredis');\n\nclass DockerTestManager {\n constructor() {\n this.environment = null;\n this.config = {\n pgPort: null,\n redisPort: null\n };\n }\n\n async start() {\n this.environment = await new DockerComposeEnvironment(\n '.', \n 'docker-compose.test.yml'\n ).withWaitStrategy('postgres_test', Wait.forHealthCheck())\n .withWaitStrategy('redis_test', Wait.forHealthCheck())\n .up();\n\n // 提取端口映射\n const pgContainer = this.environment.getContainer('postgres_test');\n const redisContainer = this.environment.getContainer('redis_test');\n \n this.config.pgPort = pgContainer.getMappedPort(5432);\n this.config.redisPort = redisContainer.getMappedPort(6379);\n \n return this.config;\n }\n\n async stop() {\n if (this.environment) {\n await this.environment.down();\n }\n }\n}\n\nmodule.exports = DockerTestManager;\n\n高级集成测试场景
\n\n现在,让我们转向具体的测试,这些测试检查组件之间的真实交互。
\n