testing-standards
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePHP 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.phptests/
├── Unit/ # 单元测试(独立无依赖)
│ ├── Service/
│ │ └── UserServiceTest.php
│ └── Entity/
│ └── UserTest.php
├── Integration/ # 集成测试(多组件协作)
│ └── Repository/
│ └── UserRepositoryTest.php
└── Functional/ # 功能测试(端到端)
└── Api/
└── UserApiTest.phpTest 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: or are ONLY allowed for features planned for future implementation.
@skipmarkTestSkipped()严格规则:仅当测试针对计划在未来实现的功能时,才允许使用或。
@skipmarkTestSkipped()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:
- Fix the test: Update assertions, add Fixture data, mock external dependencies correctly
- Fix production code: If behavior is wrong, fix the implementation
- Remove the test: If testing non-existent feature, delete the test entirely
If test is difficult:
- Break down the test: Split complex test into smaller, focused tests
- Improve test infrastructure: Add helpers, factories, or fixtures
- Mock external dependencies: Email, API calls, file I/O should be mocked
- Ask for help: Don't skip - seek assistance to write proper test
If feature doesn't exist:
- Don't write the test: Only test actual production code
- Future features: Only add @skip with ticket/version reference
- Delete misaligned tests: If test references non-existent code, remove it
如果测试失败:
- 修复测试:更新断言、添加测试数据、正确模拟外部依赖
- 修复生产代码:如果行为错误,修复实现
- 删除测试:如果测试针对不存在的功能,直接删除
如果测试编写困难:
- 拆分测试:将复杂测试拆分为更小、聚焦的测试
- 改进测试基础设施:添加辅助工具、工厂类或测试数据
- 模拟外部依赖:邮件、API调用、文件IO等都应被模拟
- 寻求帮助:不要跳过 - 寻求协助编写合适的测试
如果功能不存在:
- 不要编写测试:仅针对实际存在的生产代码编写测试
- 未来功能:仅在带有工单/版本参考时添加@skip
- 删除不匹配的测试:如果测试引用不存在的代码,删除它
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
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
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
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 failuresTEST_ONLY="--filter testMethodName ./tests/TestCase/Service/UserServiceTest.php"
docker compose -f docker-compose.test.yml run --rm web
docker compose -f docker-compose.test.yml run --rm web
**为何Docker执行至关重要:**
- 确保所有环境中PHP版本一致
- 避免本地与CI/CD环境的版本不匹配
- 保证数据库配置相同
- 防止环境特定的测试失败Prohibited Execution Methods
禁止的执行方式
bash
undefinedbash
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
undefinedphp vendor/bin/phpunit
undefinedPre-Execution Checklist
执行前检查清单
Before running tests, verify:
- exists in project root
docker-compose.test.yml - or
Dockerfilespecifies correct PHP versionDockerfile.test - Check for project-specific instructions
tests/README.md - Verify test database configuration
运行测试前,请验证:
- 项目根目录下存在
docker-compose.test.yml - 或
Dockerfile指定了正确的PHP版本Dockerfile.test - 查看获取项目特定的说明
tests/README.md - 验证测试数据库配置
Environment Configuration
环境配置示例
yaml
undefinedyaml
undefineddocker-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
undefinedversion: '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
undefinedFramework-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