
本文详解 Symfony + Docker 环境下 PHPUnit 测试无法连接数据库的根本原因(如 SQLSTATE[HY000] [2002] getaddrinfo failed),聚焦 database 主机名解析失败、测试专用数据库后缀机制、环境变量隔离等核心问题,并提供可立即落地的配置修正、验证命令与最佳实践。
本文详解 symfony + docker 环境下 phpunit 测试无法连接数据库的根本原因(如 `sqlstate[hy000] [2002] getaddrinfo failed`),聚焦 `database` 主机名解析失败、测试专用数据库后缀机制、环境变量隔离等核心问题,并提供可立即落地的配置修正、验证命令与最佳实践。
在 Symfony 项目迁移到 Docker 后,常出现「应用正常连库,但 PHPUnit 测试报错 SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for database failed: Name or service not known」——这并非网络不通,而是测试环境未正确继承或解析 Docker 网络服务名,叠加 Symfony 默认启用的测试专用数据库后缀机制共同导致的典型故障。
? 根本原因拆解
你的 docker-compose.yml 正确声明了 database 服务,且 app 容器通过 DATABASE_URL=mysql://root:root@database:3306/app 能正常访问,说明 Docker 内网 DNS 解析和网络连通性本身无问题。但 PHPUnit 测试运行时,默认不复用 app 容器的运行时环境:它通常由本地 CLI 或 CI 环境直接执行(如 php ./vendor/bin/phpunit),此时 @database 这个主机名在宿主机上根本不存在,DNS 解析必然失败。
更关键的是:Symfony 的测试环境(APP_ENV=test)会自动启用 Doctrine 的数据库隔离策略——它不会直接使用 .env 或 env.test 中的 DATABASE_URL 创建/连接 app 库,而是动态追加后缀(如 _test)并尝试连接 app_test。若该库不存在,或连接参数仍指向 @database(宿主机不可达),就会触发上述错误。
✅ 正确解决方案(三步闭环)
1. 确保测试环境使用正确的数据库主机与端口
不要依赖宿主机解析 database,而应为测试显式指定 可被宿主机访问的 MySQL 地址:
- ✅ 推荐方案(开发机直连):将 env.test 中的 DATABASE_URL 改为宿主机 IP 或 host.docker.internal(Docker Desktop macOS/Windows 支持,Linux 需手动添加):
DATABASE_URL="mysql://root:root@host.docker.internal:3306/app_test"
- ✅ 备选方案(Docker Compose 网络桥接):若坚持容器内执行测试(如 docker-compose run --rm app php ./vendor/bin/phpunit),需在 docker-compose.yml 中为 app 服务显式添加 network_mode: "bridge" 或确保其与 database 共享网络(你当前已通过默认 bridge 实现,但需确认测试命令在容器内执行)。
⚠️ 注意:.env 文件仅影响 dev/prod 环境;env.test 才是测试环境的配置源。请删除 .env 中的 DATABASE_URL,避免干扰。
2. 显式创建并配置测试专用数据库
Symfony 测试环境默认期望数据库名为 app_test(由 dbname_suffix: '_test' 控制)。务必手动创建并授权:
# 在宿主机执行(假设 MySQL 运行在 3306) mysql -h 127.0.0.1 -P 3306 -u root -proot -e "CREATE DATABASE IF NOT EXISTS app_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" mysql -h 127.0.0.1 -P 3306 -u root -proot -e "GRANT ALL PRIVILEGES ON app_test.* TO 'root'@'%'; FLUSH PRIVILEGES;"
验证配置是否生效:
php bin/console debug:config doctrine --env=test | grep -A5 "dbname_suffix\|url" # 输出应包含:dbname_suffix: '_test' 和 url: 'mysql://root:root@host.docker.internal:3306/app_test'
3. 强制重建测试数据库结构(非 schema:update)
切勿使用 doctrine:schema:update --force(生产禁用,且跳过迁移历史)。正确流程:
# 1. 清空旧测试库(可选) php bin/console doctrine:database:drop --force --if-exists --env=test # 2. 创建新测试库(使用 env.test 中的 DATABASE_URL) php bin/console doctrine:database:create --env=test # 3. 同步实体结构(生成 SQL 并执行) php bin/console doctrine:schema:create --env=test # 4. (推荐)改用迁移方式,保障一致性 php bin/console make:migration --env=test php bin/console doctrine:migrations:migrate --no-interaction --env=test
?️ 关键注意事项
-
环境变量隔离:phpunit.xml.dist 中
确保加载 env.test,但需确认该文件被 tests/bootstrap.php 正确读取(Symfony 标准项目已内置)。 - Docker 网络验证:若坚持容器内运行测试,执行 docker-compose exec app ping database 确认连通性;失败则检查 docker-compose.yml 是否遗漏 networks 显式定义。
- 密码特殊字符:root:root 无特殊字符,但若后续修改为 pass@word,必须 URL 编码为 pass%40word。
- PHP 扩展检查:在 app 容器内执行 php -m | grep pdo_mysql,确保 pdo_mysql 已启用(你的 Dockerfile 已正确安装)。
✅ 最终验证命令
# 1. 宿主机直连测试库 mysql -h 127.0.0.1 -P 3306 -u root -proot app_test -e "SHOW TABLES;" # 2. 运行单个测试(跳过数据库操作先验证环境) php ./vendor/bin/phpunit --filter=testBasicConnection tests/Functional/ # 3. 全量测试 php ./vendor/bin/phpunit
遵循以上步骤,99% 的 Symfony+Docker+PHPUnit 数据库连接问题将被根治。核心要义:区分运行环境(宿主机 vs 容器)、明确数据库命名策略、主动创建隔离库、拒绝魔法命令,拥抱可重复的迁移流程。











