本文目录导读:

搭建PHP项目的集成测试环境是一个系统性的工程,需要从环境隔离、依赖管理、数据库策略、测试框架和CI/CD五个维度来完善。
以下是详细的搭建指南,分为基础环境、数据库策略、自动化测试和CI/CD四个部分:
第一阶段:基础环境隔离(本地开发)
这一步是为了确保每个人和CI服务器能在一致的环境中运行。
-
使用 Docker Compose(强烈推荐) 这是目前最主流的做法,它能保证开发、测试和生产环境的“一致性”。
-
目录结构:
project/ ├── docker/ │ ├── php/ │ │ └── Dockerfile │ ├── nginx/ │ │ └── default.conf │ └── mysql/ │ └── init.sql # 测试数据初始化脚本 ├── docker-compose.yml ├── src/ # 你的项目代码 └── tests/ # 测试代码
-
docker-compose.yml示例:version: '3.8' services: php-fpm: build: ./docker/php volumes: - ./src:/var/www/html environment: - APP_ENV=testing - DB_HOST=mysql depends_on: - mysql nginx: image: nginx:alpine ports: - "8080:80" # 避免与本地环境冲突 volumes: - ./src:/var/www/html - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - php-fpm mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: integration_test volumes: - ./docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro ports: - "3306:3306" # 注意:这里做端口映射,如果本地有MySQL,请换一个端口,33061:3306
-
-
环境变量管理(.env) 在测试环境下,我们需要强制加载
.env.testing文件,切勿把测试配置写在.env里,避免误操作生产数据库,可以通过环境变量注入或PHP代码动态加载。// 你项目的入口文件或配置文件中(bootstrap.php) if (getenv('APP_ENV') === 'testing') { $dotenv = Dotenv\Dotenv::createImmutable(__DIR__, '.env.testing'); $dotenv->load(); }
第二阶段:数据库隔离策略(关键)
集成测试最危险的是污染开发数据库,因此我们要确保测试数据与业务数据分离。
-
方案A:独立的测试数据库(推荐)
- 在
docker-compose.yml中,不映射MySQL 3306端口到宿主机(或者在配置文件中通过变量区分),或者直接创建多个数据库。 - 做法:在
.env.testing中配置DB_DATABASE=integration_test,确保应用实例使用的是这个数据库。 - 优点:速度快,接近真实环境。
- 缺点:测试数据可能残留,需要在测试前后清理。
- 在
-
方案B:事务回滚(最安全,但速度稍慢)
- 在测试基类中,利用 PHPUnit 或 Codeception 的
DatabaseTransactionsTrait。 - 原理:每个测试用例开始前开启事务,结束时回滚,数据库状态“永远”停留在初始状态。
// PHPUnit + Laravel 写法示例 use Illuminate\Foundation\Testing\DatabaseTransactions;
class UserIntegrationTest extends TestCase { use DatabaseTransactions; // 测试结束后自动回滚
public function testUserCreation() { // 创建用户的操作 $response = $this->post('/api/users', [...]); $response->assertStatus(201); } - 在测试基类中,利用 PHPUnit 或 Codeception 的
-
初始化数据迁移与填充
- 在测试启动脚本中,必须执行:
php artisan migrate:fresh --env=testing php artisan db:seed --env=testing # 或者使用测试专用的 Seed 类 php artisan db:seed --class=IntegrationTestSeeder --env=testing
- 在测试启动脚本中,必须执行:
第三阶段:搭建测试框架(工具链)
-
选择测试工具(根据项目现状选择):
- PHPUnit:最基础、最底层的单元/集成测试框架(几乎所有框架都内置)。
- Codeception:更适合做端到端的集成测试,支持模拟浏览器行为(如填表、点击按钮)。
- Behat:如果你实施BDD(行为驱动开发),则使用它。
-
启动脚本配置(
phpunit.xml) 在项目根目录的phpunit.xml中配置测试专用的环境变量,而不是直接手改.env。<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Integration"> <directory>./tests/Integration</directory> </testsuite> </testsuites> <php> <!-- 这是关键:强制测试环境使用独立的环境变量 --> <env name="APP_ENV" value="testing"/> <env name="DB_CONNECTION" value="mysql"/> <env name="DB_DATABASE" value="integration_test"/> <env name="CACHE_DRIVER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <!-- 让队列任务在测试中立即执行 --> </php> </phpunit> -
模拟外部服务(Mocking 与 Fake) 集成测试的“集成”指的不仅仅是数据库,还包括第三方API(如支付平台、物流平台),如果不希望测试调用真实的支付宝接口,需要用到 Mock。
-
使用 PHPUnit 的 Mockery:
$paymentGateway = Mockery::mock(PaymentGateway::class); $paymentGateway->shouldReceive('charge')->once()->andReturn(['status' => 'success']); $service = new OrderService($paymentGateway); $result = $service->checkout(100); -
HTTP 模拟:如果项目使用 Guzzle,可以用
Http::fake()(Laravel自带)或GuzzleHttp\Handler\MockHandler。
-
第四阶段:CI/CD 持续集成(自动化流水线)
这一步可以在 GitHub Actions 或 GitLab CI 中配置,当代码推送后,自动搭建环境并执行测试。
-
GitHub Actions 示例(
.github/workflows/integration.yml):name: PHP Integration Tests on: [push, pull_request] jobs: php-integration: runs-on: ubuntu-latest services: mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: integration_test ports: - 3306:3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 steps: - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.3' extensions: pdo, mysqli - name: Install Composer Dependencies run: composer install --no-interaction --prefer-dist - name: Run Database Migrations (在 CI 环境下的测试库操作) run: php artisan migrate --env=testing --force - name: Run Integration Tests run: vendor/bin/phpunit --testsuite Integration --coverage-text env: DB_HOST: 127.0.0.1 DB_PORT: 3306 DB_DATABASE: integration_test DB_USERNAME: root DB_PASSWORD: secret -
关键点:
- 在 CI 中,我们走的是
phpunit.xml中的<env>设置,或者由 CI 系统提供的env注入。 - 数据库必须使用干净的迁移(
migrate:fresh)来保证准确性。
- 在 CI 中,我们走的是
补充:核心技巧与注意事项
-
必须清理缓存: 配置变更后,需要确保测试代码不使用旧缓存。
php artisan config:clear --env=testing
-
邮件与日志: 在测试环境,将邮件设为
log驱动,避免真的发送邮件。// .env.testing MAIL_MAILER=log LOG_CHANNEL=single
-
文件存储: 测试时生成的文件应放在本地临时目录(
storage/app/testing),而不是云存储(S3等)。
总结流程图
开发者 push 代码
↓
CI 服务器 (GitHub Actions / Jenkins)
↓
启动 Docker MySQL 容器 (挂载空数据卷)
↓
执行 Composer Install
↓
执行 php artisan migrate --env=testing (重建结构)
↓
执行 php artisan db:seed --class=TestDataSeeder (填充基础数据)
↓
运行 PHPUnit / Codeception 测试套件
↓
测试通过 → 部署到预发布环境 / 失败 → 发送通知
核心原则:隔离性(独立数据库、独立缓存、独立文件存储)和可重复性(每次构建环境一致),如果你能搭建好这套流程,项目的集成测试就能高效、安全地稳定运行。