testing-standards

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PHP Testing Standards

PHP测试标准

Language-level testing standards using PHPUnit, applicable to any PHP project.
基于PHPUnit的语言级测试标准,适用于所有PHP项目。

PHPUnit Basics

PHPUnit基础

Test Class Structure

测试类结构

php
<?php
declare(strict_types=1);

namespace App\Test\TestCase\Service;

use PHPUnit\Framework\TestCase;
use App\Service\UserService;

/**
 * UserService Test
 *
 * Tests user service functionality
 *
 * @covers \App\Service\UserService
 */
class UserServiceTest extends TestCase
{
    private UserService $service;

    protected function setUp(): void
    {
        parent::setUp();
        $this->service = new UserService();
    }

    protected function tearDown(): void
    {
        unset($this->service);
        parent::tearDown();
    }

    public function testSomething(): void
    {
        // Test implementation
    }
}
php
<?php
declare(strict_types=1);

namespace App\Test\TestCase\Service;

use PHPUnit\Framework\TestCase;
use App\Service\UserService;

/**
 * UserService Test
 *
 * Tests user service functionality
 *
 * @covers \App\Service\UserService
 */
class UserServiceTest extends TestCase
{
    private UserService $service;

    protected function setUp(): void
    {
        parent::setUp();
        $this->service = new UserService();
    }

    protected function tearDown(): void
    {
        unset($this->service);
        parent::tearDown();
    }

    public function testSomething(): void
    {
        // Test implementation
    }
}

Test Method Naming

测试方法命名

php
// Pattern: test + MethodName + Scenario
public function testValidateUserReturnsTrue

WhenDataIsValid(): void {}
public function testValidateUserReturnsFalseWhenEmailInvalid(): void {}
public function testValidateUserThrowsExceptionWhenAgeNegative(): void {}

// Alternative: use @test annotation
/**
 * @test
 */
public function it_validates_user_with_valid_data(): void {}
php
// 模式:test + 方法名 + 场景
public function testValidateUserReturnsTrue

WhenDataIsValid(): void {}
public function testValidateUserReturnsFalseWhenEmailInvalid(): void {}
public function testValidateUserThrowsExceptionWhenAgeNegative(): void {}

// 替代方案:使用@test注解
/**
 * @test
 */
public function it_validates_user_with_valid_data(): void {}

Assertions

断言

Common Assertions

常用断言

php
// Equality
$this->assertEquals($expected, $actual);
$this->assertSame($expected, $actual);  // Strict comparison
$this->assertNotEquals($expected, $actual);

// Boolean
$this->assertTrue($condition);
$this->assertFalse($condition);

// Null
$this->assertNull($value);
$this->assertNotNull($value);

// Empty/Count
$this->assertEmpty($array);
$this->assertNotEmpty($array);
$this->assertCount(3, $array);

// String
$this->assertStringContainsString('needle', $haystack);
$this->assertStringStartsWith('prefix', $string);
$this->assertStringEndsWith('suffix', $string);
$this->assertMatchesRegularExpression('/pattern/', $string);

// Array
$this->assertContains('value', $array);
$this->assertArrayHasKey('key', $array);
$this->assertArraySubset($subset, $array);

// Object
$this->assertInstanceOf(User::class, $object);
$this->assertObjectHasAttribute('property', $object);

// Exception
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid data');
php
// 相等性
$this->assertEquals($expected, $actual);
$this->assertSame($expected, $actual);  // 严格比较
$this->assertNotEquals($expected, $actual);

// 布尔值
$this->assertTrue($condition);
$this->assertFalse($condition);

// 空值
$this->assertNull($value);
$this->assertNotNull($value);

// 空/数量
$this->assertEmpty($array);
$this->assertNotEmpty($array);
$this->assertCount(3, $array);

// 字符串
$this->assertStringContainsString('needle', $haystack);
$this->assertStringStartsWith('prefix', $string);
$this->assertStringEndsWith('suffix', $string);
$this->assertMatchesRegularExpression('/pattern/', $string);

// 数组
$this->assertContains('value', $array);
$this->assertArrayHasKey('key', $array);
$this->assertArraySubset($subset, $array);

// 对象
$this->assertInstanceOf(User::class, $object);
$this->assertObjectHasAttribute('property', $object);

// 异常
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid data');

Custom Assertions

自定义断言

php
// Add descriptive messages
$this->assertEquals(
    $expected,
    $actual,
    'User name should match expected value'
);

// Use assertThat for complex conditions
$this->assertThat(
    $value,
    $this->logicalAnd(
        $this->greaterThan(0),
        $this->lessThan(100)
    )
);
php
// 添加描述性消息
$this->assertEquals(
    $expected,
    $actual,
    '用户名应与预期值匹配'
);

// 使用assertThat处理复杂条件
$this->assertThat(
    $value,
    $this->logicalAnd(
        $this->greaterThan(0),
        $this->lessThan(100)
    )
);

Test Patterns

测试模式

AAA Pattern (Arrange-Act-Assert)

AAA模式(准备-执行-断言)

php
public function testUserCreation(): void
{
    // Arrange - Set up test data
    $userData = [
        'name' => 'John Doe',
        'email' => 'john@example.com',
    ];

    // Act - Execute the code under test
    $user = $this->service->createUser($userData);

    // Assert - Verify the outcome
    $this->assertInstanceOf(User::class, $user);
    $this->assertEquals('John Doe', $user->getName());
    $this->assertEquals('john@example.com', $user->getEmail());
}
php
public function testUserCreation(): void
{
    // 准备 - 设置测试数据
    $userData = [
        'name' => 'John Doe',
        'email' => 'john@example.com',
    ];

    // 执行 - 运行被测代码
    $user = $this->service->createUser($userData);

    // 断言 - 验证结果
    $this->assertInstanceOf(User::class, $user);
    $this->assertEquals('John Doe', $user->getName());
    $this->assertEquals('john@example.com', $user->getEmail());
}

Given-When-Then Pattern

Given-When-Then模式

php
public function testUserLogin(): void
{
    // Given - Initial context
    $user = $this->createAuthenticatedUser();
    $credentials = ['email' => 'test@example.com', 'password' => 'secret'];

    // When - Event occurs
    $result = $this->service->login($credentials);

    // Then - Expected outcome
    $this->assertTrue($result->isSuccess());
    $this->assertEquals($user->getId(), $result->getUserId());
}
php
public function testUserLogin(): void
{
    // Given - 初始上下文
    $user = $this->createAuthenticatedUser();
    $credentials = ['email' => 'test@example.com', 'password' => 'secret'];

    // When - 触发事件
    $result = $this->service->login($credentials);

    // Then - 预期结果
    $this->assertTrue($result->isSuccess());
    $this->assertEquals($user->getId(), $result->getUserId());
}

Data Providers

数据提供者

Basic Data Provider

基础数据提供者

php
/**
 * @dataProvider validEmailProvider
 */
public function testValidateEmailWithValidData(string $email): void
{
    $result = $this->validator->validateEmail($email);
    $this->assertTrue($result);
}

public function validEmailProvider(): array
{
    return [
        'standard email' => ['user@example.com'],
        'subdomain' => ['user@mail.example.com'],
        'plus addressing' => ['user+tag@example.com'],
    ];
}
php
/**
 * @dataProvider validEmailProvider
 */
public function testValidateEmailWithValidData(string $email): void
{
    $result = $this->validator->validateEmail($email);
    $this->assertTrue($result);
}

public function validEmailProvider(): array
{
    return [
        '标准邮箱' => ['user@example.com'],
        '子域名邮箱' => ['user@mail.example.com'],
        '加号寻址邮箱' => ['user+tag@example.com'],
    ];
}

Multiple Parameters

多参数数据提供者

php
/**
 * @dataProvider userDataProvider
 */
public function testUserValidation(string $name, int $age, bool $expectedResult): void
{
    $result = $this->validator->validate($name, $age);
    $this->assertEquals($expectedResult, $result);
}

public function userDataProvider(): array
{
    return [
        'valid user' => ['John Doe', 25, true],
        'empty name' => ['', 25, false],
        'negative age' => ['John Doe', -1, false],
        'too young' => ['John Doe', 15, false],
    ];
}
php
/**
 * @dataProvider userDataProvider
 */
public function testUserValidation(string $name, int $age, bool $expectedResult): void
{
    $result = $this->validator->validate($name, $age);
    $this->assertEquals($expectedResult, $result);
}

public function userDataProvider(): array
{
    return [
        '有效用户' => ['John Doe', 25, true],
        '空用户名' => ['', 25, false],
        '年龄为负' => ['John Doe', -1, false],
        '年龄过小' => ['John Doe', 15, false],
    ];
}

Test Doubles

测试替身

Mocks

模拟对象(Mocks)

php
public function testUserServiceCallsRepository(): void
{
    // Create mock
    $repository = $this->createMock(UserRepository::class);

    // Set expectations
    $repository->expects($this->once())
        ->method('findById')
        ->with(1)
        ->willReturn(new User(['id' => 1, 'name' => 'John']));

    // Inject mock
    $service = new UserService($repository);

    // Execute and assert
    $user = $service->getUser(1);
    $this->assertEquals('John', $user->getName());
}
php
public function testUserServiceCallsRepository(): void
{
    // 创建模拟对象
    $repository = $this->createMock(UserRepository::class);

    // 设置预期
    $repository->expects($this->once())
        ->method('findById')
        ->with(1)
        ->willReturn(new User(['id' => 1, 'name' => 'John']));

    // 注入模拟对象
    $service = new UserService($repository);

    // 执行并断言
    $user = $service->getUser(1);
    $this->assertEquals('John', $user->getName());
}

Stubs

存根(Stubs)

php
public function testUserServiceWithStub(): void
{
    // Create stub (no expectations)
    $repository = $this->createStub(UserRepository::class);

    // Configure return values
    $repository->method('findAll')
        ->willReturn([
            new User(['id' => 1, 'name' => 'John']),
            new User(['id' => 2, 'name' => 'Jane']),
        ]);

    $service = new UserService($repository);
    $users = $service->getAllUsers();

    $this->assertCount(2, $users);
}
php
public function testUserServiceWithStub(): void
{
    // 创建存根(无预期)
    $repository = $this->createStub(UserRepository::class);

    // 配置返回值
    $repository->method('findAll')
        ->willReturn([
            new User(['id' => 1, 'name' => 'John']),
            new User(['id' => 2, 'name' => 'Jane']),
        ]);

    $service = new UserService($repository);
    $users = $service->getAllUsers();

    $this->assertCount(2, $users);
}

Spy

间谍(Spy)

php
public function testEventWasTriggered(): void
{
    // Create spy
    $eventDispatcher = $this->createMock(EventDispatcher::class);

    // Verify method was called
    $eventDispatcher->expects($this->once())
        ->method('dispatch')
        ->with($this->isInstanceOf(UserCreatedEvent::class));

    $service = new UserService($eventDispatcher);
    $service->createUser(['name' => 'John']);
}
php
public function testEventWasTriggered(): void
{
    // 创建间谍
    $eventDispatcher = $this->createMock(EventDispatcher::class);

    // 验证方法是否被调用
    $eventDispatcher->expects($this->once())
        ->method('dispatch')
        ->with($this->isInstanceOf(UserCreatedEvent::class));

    $service = new UserService($eventDispatcher);
    $service->createUser(['name' => 'John']);
}

Test Documentation

测试文档

PHPDoc for Tests

测试用PHPDoc

php
/**
 * Test user validation with invalid email
 *
 * Verifies that validation fails when email format is invalid
 *
 * @return void
 * @throws \Exception
 */
public function testValidateUserWithInvalidEmail(): void
{
    $this->expectException(\InvalidArgumentException::class);
    $this->validator->validate('invalid-email');
}
php
/**
 * 测试使用无效邮箱的用户验证
 *
 * 验证当邮箱格式无效时,验证会失败
 *
 * @return void
 * @throws \Exception
 */
public function testValidateUserWithInvalidEmail(): void
{
    $this->expectException(\InvalidArgumentException::class);
    $this->validator->validate('invalid-email');
}

Test Class Documentation

测试类文档

php
/**
 * User Service Test
 *
 * Comprehensive tests for UserService class including:
 * - User creation and validation
 * - Email verification
 * - Password hashing
 * - User authentication
 *
 * @covers \App\Service\UserService
 * @uses \App\Repository\UserRepository
 * @uses \App\Entity\User
 */
class UserServiceTest extends TestCase
{
    // Test methods
}
php
/**
 * 用户服务测试
 *
 * 针对UserService类的全面测试,包括:
 * - 用户创建与验证
 * - 邮箱验证
 * - 密码哈希
 * - 用户认证
 *
 * @covers \App\Service\UserService
 * @uses \App\Repository\UserRepository
 * @uses \App\Entity\User
 */
class UserServiceTest extends TestCase
{
    // 测试方法
}

Test Organization

测试组织

Test File Structure

测试文件结构

tests/
├── Unit/              # Unit tests (isolated, no dependencies)
│   ├── Service/
│   │   └── UserServiceTest.php
│   └── Entity/
│       └── UserTest.php
├── Integration/       # Integration tests (multiple components)
│   └── Repository/
│       └── UserRepositoryTest.php
└── Functional/        # Functional tests (end-to-end)
    └── Api/
        └── UserApiTest.php
tests/
├── Unit/              # 单元测试(独立无依赖)
│   ├── Service/
│   │   └── UserServiceTest.php
│   └── Entity/
│       └── UserTest.php
├── Integration/       # 集成测试(多组件协作)
│   └── Repository/
│       └── UserRepositoryTest.php
└── Functional/        # 功能测试(端到端)
    └── Api/
        └── UserApiTest.php

Test Categories

测试分类

php
/**
 * @group unit
 * @group user
 */
class UserServiceTest extends TestCase {}

/**
 * @group integration
 * @group database
 */
class UserRepositoryTest extends TestCase {}
php
/**
 * @group unit
 * @group user
 */
class UserServiceTest extends TestCase {}

/**
 * @group integration
 * @group database
 */
class UserRepositoryTest extends TestCase {}

Coverage

代码覆盖率

Code Coverage Requirements

代码覆盖率要求

php
/**
 * @covers \App\Service\UserService::createUser
 * @covers \App\Service\UserService::validateUser
 */
class UserServiceTest extends TestCase {}
php
/**
 * @covers \App\Service\UserService::createUser
 * @covers \App\Service\UserService::validateUser
 */
class UserServiceTest extends TestCase {}

Ignore from Coverage

排除覆盖率统计

php
/**
 * @codeCoverageIgnore
 */
class DeprecatedClass {}

/**
 * @codeCoverageIgnore
 */
public function legacyMethod(): void {}
php
/**
 * @codeCoverageIgnore
 */
class DeprecatedClass {}

/**
 * @codeCoverageIgnore
 */
public function legacyMethod(): void {}

Best Practices

最佳实践

DO

建议做法

php
// ✅ Test one thing per test
public function testUserNameValidation(): void {}
public function testUserEmailValidation(): void {}

// ✅ Use descriptive test names
public function testValidateUserThrowsExceptionWhenEmailIsEmpty(): void {}

// ✅ Use data providers for similar test cases
/**
 * @dataProvider invalidEmailProvider
 */
public function testInvalidEmails(string $email): void {}

// ✅ Clean up in tearDown
protected function tearDown(): void {
    $this->cleanupTestData();
    parent::tearDown();
}
php
// ✅ 每个测试只验证一件事
public function testUserNameValidation(): void {}
public function testUserEmailValidation(): void {}

// ✅ 使用描述性的测试名称
public function testValidateUserThrowsExceptionWhenEmailIsEmpty(): void {}

// ✅ 对相似测试用例使用数据提供者
/**
 * @dataProvider invalidEmailProvider
 */
public function testInvalidEmails(string $email): void {}

// ✅ 在tearDown中清理资源
protected function tearDown(): void {
    $this->cleanupTestData();
    parent::tearDown();
}

DON'T

不建议做法

php
// ❌ Test multiple things in one test
public function testEverything(): void {
    $this->testValidation();
    $this->testCreation();
    $this->testDeletion();
}

// ❌ Use generic test names
public function testMethod1(): void {}

// ❌ Leave test data
public function testWithoutCleanup(): void {
    // Creates test data but never cleans up
}

// ❌ Skip tests without reason
public function testSomething(): void {
    $this->markTestSkipped();  // Why?
}
php
// ❌ 一个测试验证多件事
public function testEverything(): void {
    $this->testValidation();
    $this->testCreation();
    $this->testDeletion();
}

// ❌ 使用通用的测试名称
public function testMethod1(): void {}

// ❌ 遗留测试数据
public function testWithoutCleanup(): void {
    // 创建测试数据但从不清理
}

// ❌ 无理由跳过测试
public function testSomething(): void {
    $this->markTestSkipped();  // 原因?
}

Skipped Test Policy

跳过测试规则

STRICT RULE:
@skip
or
markTestSkipped()
are ONLY allowed for features planned for future implementation.
严格规则:仅当测试针对计划在未来实现的功能时,才允许使用
@skip
markTestSkipped()

Prohibited Patterns

禁止的模式

php
// ❌ PROHIBITED: Skipping due to complexity
/**
 * @skip Feature is too complex to test
 */
public function testComplexFeature(): void {
    $this->markTestSkipped('Too complex');
}

// ❌ PROHIBITED: Skipping due to incomplete fixtures
/**
 * @skip Fixture data incomplete
 */
public function testWithIncompleteFixture(): void {
    $this->markTestSkipped('Fixture incomplete');
}

// ❌ PROHIBITED: Skipping due to missing dependencies
public function testExternalApiIntegration(): void {
    $this->markTestSkipped('API not available in test env');
}

// ❌ PROHIBITED: Skipping due to intermittent failures
public function testFlaky(): void {
    $this->markTestSkipped('Test is flaky');
}
php
// ❌ 禁止:因复杂度跳过
/**
 * @skip 功能过于复杂无法测试
 */
public function testComplexFeature(): void {
    $this->markTestSkipped('Too complex');
}

// ❌ 禁止:因测试数据不完整跳过
/**
 * @skip 测试数据不完整
 */
public function testWithIncompleteFixture(): void {
    $this->markTestSkipped('Fixture incomplete');
}

// ❌ 禁止:因依赖缺失跳过
public function testExternalApiIntegration(): void {
    $this->markTestSkipped('测试环境中API不可用');
}

// ❌ 禁止:因间歇性失败跳过
public function testFlaky(): void {
    $this->markTestSkipped('测试不稳定');
}

Allowed Pattern (ONLY for confirmed future features)

允许的模式(仅针对已确认的未来功能)

php
// ✅ ALLOWED: Future feature with version/milestone reference
/**
 * @skip File upload feature will be implemented in v2.0 (TICKET-123)
 */
public function testFileUploadValidation(): void {
    $this->markTestSkipped('File upload feature planned for v2.0 - see TICKET-123');
}
php
// ✅ 允许:带有版本/里程碑参考的未来功能
/**
 * @skip 文件上传功能将在v2.0版本实现(TICKET-123)
 */
public function testFileUploadValidation(): void {
    $this->markTestSkipped('文件上传功能计划在v2.0版本实现 - 参见TICKET-123');
}

Why This Matters

为何此规则重要

  • Skipped tests hide real coverage gaps: They create false sense of completeness
  • "Temporary" skips become permanent debt: Most skipped tests are never fixed
  • Tests must validate actual production behavior NOW: If production code exists, test MUST execute it
  • Coverage metrics become meaningless: Skipped tests inflate reported coverage
  • 跳过的测试会隐藏真实的覆盖率缺口:它们会营造一种测试完整的假象
  • “临时”跳过会变成永久技术债务:大多数跳过的测试永远不会被修复
  • 测试必须验证当前的生产行为:如果生产代码已存在,测试必须执行
  • 覆盖率指标会失去意义:跳过的测试会虚增报告的覆盖率

What To Do Instead

替代方案

If test fails:
  1. Fix the test: Update assertions, add Fixture data, mock external dependencies correctly
  2. Fix production code: If behavior is wrong, fix the implementation
  3. Remove the test: If testing non-existent feature, delete the test entirely
If test is difficult:
  1. Break down the test: Split complex test into smaller, focused tests
  2. Improve test infrastructure: Add helpers, factories, or fixtures
  3. Mock external dependencies: Email, API calls, file I/O should be mocked
  4. Ask for help: Don't skip - seek assistance to write proper test
If feature doesn't exist:
  1. Don't write the test: Only test actual production code
  2. Future features: Only add @skip with ticket/version reference
  3. Delete misaligned tests: If test references non-existent code, remove it
如果测试失败
  1. 修复测试:更新断言、添加测试数据、正确模拟外部依赖
  2. 修复生产代码:如果行为错误,修复实现
  3. 删除测试:如果测试针对不存在的功能,直接删除
如果测试编写困难
  1. 拆分测试:将复杂测试拆分为更小、聚焦的测试
  2. 改进测试基础设施:添加辅助工具、工厂类或测试数据
  3. 模拟外部依赖:邮件、API调用、文件IO等都应被模拟
  4. 寻求帮助:不要跳过 - 寻求协助编写合适的测试
如果功能不存在
  1. 不要编写测试:仅针对实际存在的生产代码编写测试
  2. 未来功能:仅在带有工单/版本参考时添加@skip
  3. 删除不匹配的测试:如果测试引用不存在的代码,删除它

Enforcement

执行要求

NEVER skip to make test suite pass. Skipping is NOT a valid solution for:
  • Incomplete fixtures → Add fixture data
  • Complex logic → Break down into smaller tests
  • Flaky tests → Fix the race condition or timing issue
  • Missing mocks → Properly mock external dependencies
  • Schema mismatches → Fix migration files and clear cache
Only valid reason to skip: Documented future feature with:
  • Ticket/issue number
  • Target version or milestone
  • Explicit approval from team lead
绝对不要为了让测试套件通过而跳过测试。以下情况跳过测试不是有效解决方案:
  • 测试数据不完整 → 添加测试数据
  • 逻辑复杂 → 拆分为更小的测试
  • 测试不稳定 → 修复竞态条件或时序问题
  • 依赖缺失 → 正确模拟外部依赖
  • 架构不匹配 → 修复迁移文件并清除缓存
唯一有效的跳过理由:有文档记录的未来功能,且包含:
  • 工单/问题编号
  • 目标版本或里程碑
  • 团队负责人的明确批准

Test Execution Environment

测试执行环境

Docker-Based Test Execution (Recommended)

基于Docker的测试执行(推荐)

ALWAYS use Docker containers for PHP tests to ensure consistent PHP version and environment:
bash
undefined
始终使用Docker容器运行PHP测试,以确保PHP版本和环境的一致性:
bash
undefined

✅ CORRECT: Docker-based execution

✅ 正确:基于Docker的执行方式

docker compose -f docker-compose.test.yml run --rm web
docker compose -f docker-compose.test.yml run --rm web

With specific test file

指定测试文件

TEST_ONLY="./tests/TestCase/Service/UserServiceTest.php"
docker compose -f docker-compose.test.yml run --rm web
TEST_ONLY="./tests/TestCase/Service/UserServiceTest.php"
docker compose -f docker-compose.test.yml run --rm web

With specific test method

指定测试方法

TEST_ONLY="--filter testMethodName ./tests/TestCase/Service/UserServiceTest.php"
docker compose -f docker-compose.test.yml run --rm web

**Why Docker execution is critical:**
- Ensures consistent PHP version across all environments
- Avoids version mismatch between local and CI/CD
- Guarantees same database configuration
- Prevents environment-specific test failures
TEST_ONLY="--filter testMethodName ./tests/TestCase/Service/UserServiceTest.php"
docker compose -f docker-compose.test.yml run --rm web

**为何Docker执行至关重要:**
- 确保所有环境中PHP版本一致
- 避免本地与CI/CD环境的版本不匹配
- 保证数据库配置相同
- 防止环境特定的测试失败

Prohibited Execution Methods

禁止的执行方式

bash
undefined
bash
undefined

❌ WRONG: Direct localhost execution

❌ 错误:直接在本地执行

vendor/bin/phpunit
vendor/bin/phpunit

❌ WRONG: Composer shortcut (unless explicitly configured)

❌ 错误:使用Composer快捷方式(除非明确配置)

composer test
composer test

❌ WRONG: Direct PHPUnit without container

❌ 错误:不使用容器直接执行PHPUnit

php vendor/bin/phpunit
undefined
php vendor/bin/phpunit
undefined

Pre-Execution Checklist

执行前检查清单

Before running tests, verify:
  1. docker-compose.test.yml
    exists in project root
  2. Dockerfile
    or
    Dockerfile.test
    specifies correct PHP version
  3. Check
    tests/README.md
    for project-specific instructions
  4. Verify test database configuration
运行测试前,请验证:
  1. 项目根目录下存在
    docker-compose.test.yml
  2. Dockerfile
    Dockerfile.test
    指定了正确的PHP版本
  3. 查看
    tests/README.md
    获取项目特定的说明
  4. 验证测试数据库配置

Environment Configuration

环境配置示例

yaml
undefined
yaml
undefined

docker-compose.test.yml example

docker-compose.test.yml示例

version: '3.8' services: web: build: context: . dockerfile: Dockerfile.test environment: - DB_HOST=db - DB_DATABASE=test_database - PHP_VERSION=8.2 # Match project requirements volumes: - ./:/app depends_on: - db db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: test_database
undefined
version: '3.8' services: web: build: context: . dockerfile: Dockerfile.test environment: - DB_HOST=db - DB_DATABASE=test_database - PHP_VERSION=8.2 # 匹配项目要求 volumes: - ./:/app depends_on: - db db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: test_database
undefined

Framework-Agnostic

框架无关

These standards apply to:
  • CakePHP with PHPUnit
  • Laravel with PHPUnit
  • Symfony with PHPUnit
  • Any PHP project using PHPUnit
Framework-specific testing patterns (Fixtures, TestCase extensions, etc.) should be defined in framework-level skills (e.g.,
php-cakephp/testing-conventions
).
这些标准适用于:
  • 结合PHPUnit的CakePHP
  • 结合PHPUnit的Laravel
  • 结合PHPUnit的Symfony
  • 所有使用PHPUnit的PHP项目
框架特定的测试模式(测试数据、TestCase扩展等)应在框架级技能中定义(例如:
php-cakephp/testing-conventions
)。