4、集成测试环境搭建:Docker Compose编排PostgreSQL与消息队列容器

集成测试环境的核心挑战在于:可重复性、隔离性与数据一致性。本章使用 Docker Compose 将 PostgreSQL 与消息队列(以 RabbitMQ 为例)编排为统一的测试栈,并重点解决网络互通、端口映射与持久化卷挂载三个关键问题。

4.1 环境拓扑与容器角色

测试环境由三个容器组成:PostgreSQL 数据库、RabbitMQ 消息队列、以及测试执行器(即你的集成测试进程)。Docker Compose 确保三者启动顺序正确、网络互通、数据持久化。

关键设计原则: 测试执行器不应直接依赖宿主机服务,所有依赖项(DB、MQ)均通过 Compose 声明式管理,保证 CI/CD 与本地环境行为一致。
集成测试环境容器拓扑(Docker Compose) 测试执行器 (集成测试进程) PostgreSQL 15 端口 5432 → 宿主机 5432 RabbitMQ 3.12 端口 5672 → 宿主机 5672 共享网络:integration_test_net pg_data:/var/lib/postgresql/data rabbitmq_data:/var/lib/rabbitmq

4.2 Docker Compose 编排文件

以下 docker-compose.yml 定义了完整的测试环境。注意 depends_on 仅控制启动顺序,不保证服务就绪;因此需要配合健康检查(healthcheck)使用。

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    container_name: it-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: integration_test
      POSTGRES_USER: tester
      POSTGRES_PASSWORD: test_pass_2024
    ports:
      - "5432:5432"
    volumes:
      - pg_data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d
    networks:
      - integration_test_net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U tester -d integration_test"]
      interval: 5s
      timeout: 3s
      retries: 5

  rabbitmq:
    image: rabbitmq:3.12-management-alpine
    container_name: it-rabbitmq
    restart: unless-stopped
    environment:
      RABBITMQ_DEFAULT_USER: tester
      RABBITMQ_DEFAULT_PASS: test_pass_2024
    ports:
      - "5672:5672"
      - "15672:15672"
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    networks:
      - integration_test_net
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pg_data:
    driver: local
  rabbitmq_data:
    driver: local

networks:
  integration_test_net:
    driver: bridge

4.3 网络配置详解

所有容器加入同一个自定义桥接网络 integration_test_net,实现容器间通过服务名直接通信。测试代码中数据库连接串与 MQ 连接串使用容器服务名而非 localhost。

配置项 值 说明
网络驱动 bridge 单机测试环境默认选择,提供 DNS 解析
容器间通信 服务名:端口 例如 postgres:5432、rabbitmq:5672
宿主机访问 localhost:映射端口 仅用于调试工具(如 DBeaver、RabbitMQ 管理界面)
网络隔离 独立子网 避免与宿主机其他容器冲突
最佳实践: 测试代码中数据库与 MQ 的连接地址应通过环境变量注入,而非硬编码。Compose 文件中使用 environment 或 .env 文件传递这些变量。

4.4 端口映射策略

端口映射遵循“最小暴露”原则:仅暴露测试执行器需要访问的端口到宿主机,管理端口仅在调试时暴露。

  • PostgreSQL:5432 → 5432(数据库访问端口,必须暴露)
  • RabbitMQ:5672 → 5672(AMQP 协议端口,必须暴露)
  • RabbitMQ 管理界面:15672 → 15672(可选,调试用,CI 环境可关闭)
注意: 如果宿主机已占用 5432 或 5672 端口,请在 Compose 文件中修改左侧端口号(例如 "5433:5432"),并同步更新测试代码中的连接配置。

4.5 持久化卷配置

持久化卷保证容器重启后数据不丢失,同时支持测试数据回溯场景。每个服务使用独立命名卷,避免数据冲突。

卷名称 容器内挂载点 用途
pg_data /var/lib/postgresql/data 存储数据库文件、WAL 日志、表空间
rabbitmq_data /var/lib/rabbitmq 存储队列消息、交换机绑定、持久化消息
./init-scripts /docker-entrypoint-initdb.d 绑定挂载初始化 SQL 脚本(非持久化卷)

4.6 初始化脚本与数据准备

在 ./init-scripts 目录下放置 SQL 初始化脚本,PostgreSQL 容器首次启动时会自动执行。这是构建测试基线数据的关键入口。

-- init-scripts/01_create_tables.sql
CREATE TABLE IF NOT EXISTS orders (
    order_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS order_events (
    event_id BIGSERIAL PRIMARY KEY,
    order_id UUID REFERENCES orders(order_id),
    event_type VARCHAR(50) NOT NULL,
    payload JSONB,
    occurred_at TIMESTAMPTZ DEFAULT NOW()
);

-- 插入基线数据
INSERT INTO orders (order_id, status) VALUES
    ('a1b2c3d4-0001-4000-8000-000000000001', 'PENDING'),
    ('a1b2c3d4-0002-4000-8000-000000000002', 'CONFIRMED');

4.7 启动与验证步骤

执行以下命令启动测试环境,并验证所有服务就绪:

# 启动所有容器(后台模式)
docker compose up -d

# 查看容器状态
docker compose ps

# 查看实时日志
docker compose logs -f

# 验证 PostgreSQL 就绪
docker compose exec postgres pg_isready -U tester -d integration_test

# 验证 RabbitMQ 就绪
docker compose exec rabbitmq rabbitmq-diagnostics check_port_connectivity

# 停止并清理(保留卷数据)
docker compose down

# 完全清理(删除卷数据)
docker compose down -v
容器启动与健康检查流程 docker compose up -d 健康检查轮询 服务就绪 重试/超时失败 健康检查通过后,测试执行器方可开始连接数据库与消息队列

4.8 常见问题与避坑指南

问题1:容器启动后测试连接失败
原因:PostgreSQL 或 RabbitMQ 尚未完成初始化(健康检查未通过)。
解决:在测试代码中添加重试机制,等待健康检查通过后再建立连接。
问题2:端口冲突导致容器无法启动
原因:宿主机已占用 5432 或 5672 端口。
解决:修改 Compose 文件中的左侧端口映射,或停止占用端口的进程。
问题3:容器重启后数据丢失
原因:未正确配置持久化卷,或使用了 docker compose down -v 清除了卷。
解决:确认 volumes: 配置正确,日常使用 docker compose down(不带 -v)保留数据。

4.9 测试环境验证清单

在开始编写集成测试之前,逐项确认以下环境就绪状态:

检查项 验证命令 预期结果
PostgreSQL 容器运行中 docker compose ps postgres 状态为 Up (healthy)
RabbitMQ 容器运行中 docker compose ps rabbitmq 状态为 Up (healthy)
数据库连接正常 docker compose exec postgres psql -U tester -d integration_test -c "SELECT 1" 返回 1 行结果
MQ 连接正常 docker compose exec rabbitmq rabbitmqadmin -u tester -p test_pass_2024 list queues 返回队列列表(可能为空)
初始化数据存在 docker compose exec postgres psql -U tester -d integration_test -c "SELECT count(*) FROM orders" 返回 2 条记录
自动化建议: 将上述验证清单写入 Makefile 或 Shell 脚本,作为 CI 流水线的前置检查步骤,确保环境就绪后再执行集成测试。