phpunit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePHPUnit
PHPUnit
Test behavior, not implementation. Tests are executable documentation — if the test name doesn't explain what the code
does, rewrite it.
PHPUnit is PHP's standard testing framework. It uses test case classes extending , / for
fixtures, and a full assertion API. All patterns target PHPUnit 11+ on PHP 8.5+. Use PHP 8 attributes exclusively —
annotations are deprecated in 11, removed in 12.
TestCasesetUp()tearDown()测试行为,而非实现。测试是可执行的文档——如果测试名称无法说明代码的功能,请重写它。
PHPUnit是PHP的标准测试框架。它使用继承的测试用例类,通过/来管理测试夹具,并提供完整的断言API。所有模式均针对PHP 8.5+环境下的PHPUnit 11+版本。请仅使用PHP 8属性——注解在PHPUnit 11中已被弃用,在12中已被移除。
TestCasesetUp()tearDown()References
参考资料
- Assertion catalog, constraints, exception expectations → [] — Full assertion API grouped by category, constraint system, custom assertions
${CLAUDE_SKILL_DIR}/references/assertions.md - Test doubles — stubs, mocks, MockBuilder → [] — createStub vs createMock, return config, invocation matchers, argument constraints, MockBuilder
${CLAUDE_SKILL_DIR}/references/mocking.md - Data providers — static, named, generators → [] — #[DataProvider], #[TestWith], named datasets, generator providers, external providers
${CLAUDE_SKILL_DIR}/references/data-providers.md - phpunit.xml structure, test suites, source config → [] — XML elements, strict settings, source element, coverage reports, execution order
${CLAUDE_SKILL_DIR}/references/configuration.md
- 断言目录、约束、异常预期 → [] — 按类别分组的完整断言API、约束系统、自定义断言
${CLAUDE_SKILL_DIR}/references/assertions.md - 测试替身——桩件、模拟对象、MockBuilder → [] — createStub与createMock的区别、返回值配置、调用匹配器、参数约束、MockBuilder
${CLAUDE_SKILL_DIR}/references/mocking.md - 数据提供器——静态、命名、生成器 → [] — #[DataProvider]、#[TestWith]、命名数据集、生成器提供器、外部提供器
${CLAUDE_SKILL_DIR}/references/data-providers.md - phpunit.xml结构、测试套件、源码配置 → [] — XML元素、严格设置、源码元素、覆盖率报告、执行顺序
${CLAUDE_SKILL_DIR}/references/configuration.md
Test Structure
测试结构
Discovery and Naming
发现规则与命名规范
- Files: in configured test directories. Mirror source structure:
*Test.php→src/Service/PaymentService.php.tests/Unit/Service/PaymentServiceTest.php - Classes: . Always
final class PaymentServiceTest extends TestCase.final - Methods: prefix or
testattribute. Describe the behavior:#[Test]nottestReturnsEmptyCollectionWhenNoResults.testSearch - One test class per production class. Split into Unit/Integration directories.
- 文件命名:配置的测试目录下的文件。镜像源码结构:
*Test.php→src/Service/PaymentService.php。tests/Unit/Service/PaymentServiceTest.php - 类命名:。始终使用
final class PaymentServiceTest extends TestCase修饰。final - 方法命名:以前缀开头或使用
test属性。描述具体行为:#[Test]而非testReturnsEmptyCollectionWhenNoResults。testSearch - 一对一对应:每个生产类对应一个测试类。拆分到Unit/Integration目录下。
Arrange-Act-Assert
准备-执行-断言(Arrange-Act-Assert)
Structure every test in three phases:
php
public function testUserCreationSetsDefaults(): void
{
// Arrange
$data = ['name' => 'Alice', 'email' => 'alice@example.com'];
// Act
$user = User::fromArray($data);
// Assert
$this->assertSame('Alice', $user->getName());
$this->assertTrue($user->isActive());
$this->assertSame([], $user->getRoles());
}- One act per test. If you need multiple acts, write multiple tests.
- Comments optional when phases are obvious. Add them when the test is long enough that phases aren't immediately clear.
每个测试都分为三个阶段:
php
public function testUserCreationSetsDefaults(): void
{
// Arrange(准备)
$data = ['name' => 'Alice', 'email' => 'alice@example.com'];
// Act(执行)
$user = User::fromArray($data);
// Assert(断言)
$this->assertSame('Alice', $user->getName());
$this->assertTrue($user->isActive());
$this->assertSame([], $user->getRoles());
}- 每个测试仅一个执行步骤。如果需要多个执行步骤,请编写多个测试。
- 当阶段划分明显时可省略注释。当测试足够长导致阶段不清晰时,添加注释。
Test Granularity
测试粒度
- One concept per test. Multiple assertions are fine when they verify the same behavior. Separate tests when behaviors are independent.
- Fast by default. Unit tests should run in milliseconds. Gate slow tests behind groups: .
#[Group('slow')] - Isolation is mandatory. Tests must not depend on execution order or shared mutable state. Each test sets up its own world.
- 每个测试验证一个概念。当多个断言验证同一行为时是合理的。当行为独立时,拆分到不同测试中。
- 默认保持快速。单元测试应在毫秒级运行。将慢测试标记为组:。
#[Group('slow')] - 必须保证隔离性。测试不能依赖执行顺序或共享可变状态。每个测试都应搭建独立的测试环境。
Fixtures
测试夹具
setUp() / tearDown()
setUp() / tearDown()
- runs before each test method on a fresh instance. Create the SUT and its stubs here.
setUp() - runs after each test. Only needed for external resources (files, sockets, DB connections). Not needed for plain object cleanup.
tearDown() - /
setUpBeforeClass()run once per class. Use for expensive shared resources (DB connections). Store intearDownAfterClass()properties.static
php
final class PaymentServiceTest extends TestCase
{
private PaymentService $service;
private Gateway&Stub $gateway;
protected function setUp(): void
{
$this->gateway = $this->createStub(Gateway::class);
$this->service = new PaymentService($this->gateway);
}
}- 在每个测试方法执行前运行,基于新实例创建被测系统(SUT)及其桩件。
setUp() - 在每个测试方法执行后运行。仅在处理外部资源(文件、套接字、数据库连接)时需要。普通对象清理不需要此方法。
tearDown() - /
setUpBeforeClass()在类级别仅运行一次。用于处理昂贵的共享资源(数据库连接)。存储在tearDownAfterClass()属性中。static
php
final class PaymentServiceTest extends TestCase
{
private PaymentService $service;
private Gateway&Stub $gateway;
protected function setUp(): void
{
$this->gateway = $this->createStub(Gateway::class);
$this->service = new PaymentService($this->gateway);
}
}Fixture Lifecycle
夹具生命周期
-
— Class scope; once before first test
setUpBeforeClass() -
— Method scope; before each test
setUp() -
— Method scope; after setUp, before test
assertPreConditions() -
— Method scope; after test, before tearDown
assertPostConditions() -
— Method scope; after each test
tearDown() -
— Class scope; once after last test
tearDownAfterClass() -
Callwhen extending abstract test cases — otherwise parent fixture setup is silently skipped.
parent::setUp() -
Use/
#[Before]attributes when multiple setup methods are needed (avoids fragile#[After]chains).parent::setUp()
-
— 类作用域;在第一个测试前运行一次
setUpBeforeClass() -
— 方法作用域;在每个测试前运行
setUp() -
— 方法作用域;在setUp之后、测试前运行
assertPreConditions() -
— 方法作用域;在测试之后、tearDown前运行
assertPostConditions() -
— 方法作用域;在每个测试后运行
tearDown() -
— 类作用域;在最后一个测试后运行一次
tearDownAfterClass() -
当继承抽象测试用例时,调用— 否则父类的夹具设置会被静默跳过。
parent::setUp() -
当需要多个设置方法时,使用/
#[Before]属性 — 避免脆弱的#[After]调用链。parent::setUp()
Data Providers
数据提供器
Basic Usage
基本用法
php
use PHPUnit\Framework\Attributes\DataProvider;
#[DataProvider('additionCases')]
public function testAdd(int $a, int $b, int $expected): void
{
$this->assertSame($expected, $a + $b);
}
public static function additionCases(): array
{
return [
'zeros' => [0, 0, 0],
'positive sum' => [1, 2, 3],
'negative' => [-1, 1, 0],
];
}- Providers must be . Non-static providers are removed in PHPUnit 11.
public static - Always use named datasets — string keys produce readable failure output.
- Use attribute, not
#[DataProvider]annotation.@dataProvider
php
use PHPUnit\Framework\Attributes\DataProvider;
#[DataProvider('additionCases')]
public function testAdd(int $a, int $b, int $expected): void
{
$this->assertSame($expected, $a + $b);
}
public static function additionCases(): array
{
return [
'zeros' => [0, 0, 0],
'positive sum' => [1, 2, 3],
'negative' => [-1, 1, 0],
];
}- 提供器必须是。非静态提供器在PHPUnit 11中已被移除。
public static - 始终使用命名数据集 — 字符串键会生成可读性更强的失败输出。
- 使用属性,而非
#[DataProvider]注解。@dataProvider
Inline Data
内联数据
For small, simple datasets — no provider method needed:
php
use PHPUnit\Framework\Attributes\TestWith;
#[TestWith([0, 0, 0])]
#[TestWith([1, 2, 3])]
#[TestWith([-1, 1, 0])]
public function testAdd(int $a, int $b, int $expected): void
{
$this->assertSame($expected, $a + $b);
}适用于小型、简单的数据集——无需提供器方法:
php
use PHPUnit\Framework\Attributes\TestWith;
#[TestWith([0, 0, 0])]
#[TestWith([1, 2, 3])]
#[TestWith([-1, 1, 0])]
public function testAdd(int $a, int $b, int $expected): void
{
$this->assertSame($expected, $a + $b);
}Generator Providers
生成器提供器
For large or computed datasets:
php
public static function boundaryCases(): Generator
{
yield 'min int' => [PHP_INT_MIN, 0, PHP_INT_MIN];
yield 'max int' => [PHP_INT_MAX, 0, PHP_INT_MAX];
}适用于大型或计算型数据集:
php
public static function boundaryCases(): Generator
{
yield 'min int' => [PHP_INT_MIN, 0, PHP_INT_MIN];
yield 'max int' => [PHP_INT_MAX, 0, PHP_INT_MAX];
}Provider Rules
提供器规则
- Data must be scalar or immutable — no service objects or complex graphs in providers.
- No mock objects in providers — framework isn't initialized during provider execution.
- Empty providers are forbidden in PHPUnit 11 — throws .
InvalidDataProviderException - Multiple providers can be stacked on one test method — datasets are combined.
See for external providers, TestDox integration, and edge cases.
${CLAUDE_SKILL_DIR}/references/data-providers.md- 数据必须是标量或不可变的 — 提供器中不能包含服务对象或复杂对象图。
- 提供器中不能包含模拟对象 — 框架在提供器执行期间未初始化。
- PHPUnit 11中禁止空提供器 — 会抛出。
InvalidDataProviderException - 一个测试方法可以叠加多个提供器 — 数据集会被合并。
有关外部提供器、TestDox集成和边缘情况,请参阅。
${CLAUDE_SKILL_DIR}/references/data-providers.mdAssertions
断言
Core Assertions
核心断言
php
$this->assertSame($expected, $actual); // Strict === (preferred)
$this->assertEquals($expected, $actual); // Loose == (use sparingly)
$this->assertTrue($condition);
$this->assertFalse($condition);
$this->assertNull($value);
$this->assertInstanceOf(Expected::class, $obj);
$this->assertCount(3, $collection);
$this->assertEmpty($collection);
$this->assertArrayHasKey('key', $array);
$this->assertContains($needle, $haystack); // Strict comparison- Prefer over
assertSame()— strict type comparison catches more bugs.assertEquals() - Multiple assertions per test are fine when they verify the same behavior.
php
$this->assertSame($expected, $actual); // 严格比较 ===(推荐)
$this->assertEquals($expected, $actual); // 宽松比较 ==(谨慎使用)
$this->assertTrue($condition);
$this->assertFalse($condition);
$this->assertNull($value);
$this->assertInstanceOf(Expected::class, $obj);
$this->assertCount(3, $collection);
$this->assertEmpty($collection);
$this->assertArrayHasKey('key', $array);
$this->assertContains($needle, $haystack); // 严格比较- 优先使用而非
assertSame()— 严格类型比较能捕获更多bug。assertEquals() - 每个测试可包含多个断言,只要它们验证同一行为。
String Assertions
字符串断言
php
$this->assertStringStartsWith('Error:', $message);
$this->assertStringEndsWith('.php', $filename);
$this->assertStringContainsString('needle', $haystack);
$this->assertMatchesRegularExpression('/^\d{4}-\d{2}$/', $date);php
$this->assertStringStartsWith('Error:', $message);
$this->assertStringEndsWith('.php', $filename);
$this->assertStringContainsString('needle', $haystack);
$this->assertMatchesRegularExpression('/^\d{4}-\d{2}$/', $date);Float Comparison
浮点数比较
php
$this->assertEqualsWithDelta(3.14, $result, 0.01);php
$this->assertEqualsWithDelta(3.14, $result, 0.01);Exception Testing
异常测试
php
public function testThrowsOnInvalidInput(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('must be positive');
$calculator->divide(1, 0);
}- Call before the throwing code — it sets up the expectation.
expectException() - Use when the exception type is broad — validates the message contains the substring.
expectExceptionMessage() - Use for regex matching.
expectExceptionMessageMatches()
php
public function testThrowsOnInvalidInput(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('must be positive');
$calculator->divide(1, 0);
}- 在抛出代码之前调用— 它会设置预期条件。
expectException() - 当异常类型较宽泛时,使用— 验证消息是否包含指定子字符串。
expectExceptionMessage() - 使用进行正则匹配。
expectExceptionMessageMatches()
Deprecation / Error Expectations
弃用/错误预期
php
public function testTriggersDeprecation(): void
{
$this->expectUserDeprecationMessage('use newMethod() instead');
$service->oldMethod();
}See for the full assertion catalog, constraint system, and format string
assertions.
${CLAUDE_SKILL_DIR}/references/assertions.mdphp
public function testTriggersDeprecation(): void
{
$this->expectUserDeprecationMessage('use newMethod() instead');
$service->oldMethod();
}有关完整的断言目录、约束系统和格式字符串断言,请参阅。
${CLAUDE_SKILL_DIR}/references/assertions.mdMocking
模拟
Stubs vs Mocks
桩件(Stub)vs 模拟对象(Mock)
- Stub () — controls return values. No call verification.
createStub() - Mock () — verifies interactions (method called, arguments matched).
createMock()
Use stubs by default. Use mocks only when verifying that a side effect occurred.
- 桩件()—— 控制返回值。不验证调用。
createStub() - 模拟对象()—— 验证交互(方法是否被调用、参数是否匹配)。
createMock()
默认使用桩件。仅当需要验证副作用发生时才使用模拟对象。
Creating Stubs
创建桩件
php
$repo = $this->createStub(UserRepository::class);
$repo->method('find')->willReturn(new User(name: 'Alice'));
$service = new UserService($repo);
$result = $service->getUser(1);
$this->assertSame('Alice', $result->name);Shorthand for multiple methods:
php
$repo = $this->createConfiguredStub(UserRepository::class, [
'find' => new User(name: 'Alice'),
'exists' => true,
]);php
$repo = $this->createStub(UserRepository::class);
$repo->method('find')->willReturn(new User(name: 'Alice'));
$service = new UserService($repo);
$result = $service->getUser(1);
$this->assertSame('Alice', $result->name);多个方法的简写形式:
php
$repo = $this->createConfiguredStub(UserRepository::class, [
'find' => new User(name: 'Alice'),
'exists' => true,
]);Creating Mocks
创建模拟对象
php
$logger = $this->createMock(Logger::class);
$logger->expects($this->once())
->method('error')
->with($this->stringContains('payment failed'));
$service = new PaymentService($logger);
$service->process($invalidPayment);php
$logger = $this->createMock(Logger::class);
$logger->expects($this->once())
->method('error')
->with($this->stringContains('payment failed'));
$service = new PaymentService($logger);
$service->process($invalidPayment);Return Value Configuration
返回值配置
php
$stub->method('fetch')->willReturn('value'); // Fixed value
$stub->method('fetch')->willReturn('a', 'b', 'c'); // Consecutive values
$stub->method('fetch')->willReturnArgument(0); // Return first arg
$stub->method('fetch')->willReturnSelf(); // Fluent interface
$stub->method('fetch')->willReturnCallback(fn ($id) => "item-{$id}");
$stub->method('fetch')->willThrowException(new RuntimeException('fail'));
$stub->method('fetch')->willReturnMap([
['key1', 'value1'],
['key2', 'value2'],
]);php
$stub->method('fetch')->willReturn('value'); // 固定值
$stub->method('fetch')->willReturn('a', 'b', 'c'); // 连续值
$stub->method('fetch')->willReturnArgument(0); // 返回第一个参数
$stub->method('fetch')->willReturnSelf(); // 流畅接口
$stub->method('fetch')->willReturnCallback(fn ($id) => "item-{$id}");
$stub->method('fetch')->willThrowException(new RuntimeException('fail'));
$stub->method('fetch')->willReturnMap([
['key1', 'value1'],
['key2', 'value2'],
]);Mocking Rules
模拟规则
- Mock at boundaries. Mock external services, databases, filesystems, clocks — not internal functions.
- Don't mock what you own when a fake or in-memory implementation is available.
- Prefer dependency injection over complex mock setup. Pass collaborators as constructor parameters, stub in tests.
- Never mock the thing you're testing. If you need to mock part of the SUT, the SUT has too many responsibilities — split it.
- Favour interfaces over classes for test doubles — fewer limitations, better design.
- Do not call on stubs — deprecated in 11, error in 12.
expects()
See for MockBuilder, intersection types, invocation matchers, and PHP 8.4
property hooks.
${CLAUDE_SKILL_DIR}/references/mocking.md- 在边界处进行模拟。模拟外部服务、数据库、文件系统、时钟——而非内部函数。
- 当有假实现或内存实现可用时,不要模拟自己的代码。
- 优先使用依赖注入而非复杂的模拟设置。将协作者作为构造函数参数传递,在测试中使用桩件。
- 永远不要模拟被测对象。如果需要模拟被测系统的一部分,说明被测系统职责过多——应拆分它。
- 优先针对接口而非类创建测试替身——限制更少,设计更优。
- 不要在桩件上调用— 在PHPUnit 11中已被弃用,在12中会报错。
expects()
有关MockBuilder、交叉类型、调用匹配器和PHP 8.4属性钩子,请参阅。
${CLAUDE_SKILL_DIR}/references/mocking.mdAttributes
属性
PHPUnit 11 uses PHP 8 attributes exclusively. All attributes are in the namespace.
PHPUnit\Framework\AttributesPHPUnit 11仅使用PHP 8属性。所有属性都位于命名空间下。
PHPUnit\Framework\AttributesTest Metadata
测试元数据
- — Mark non-
#[Test]method as a testtest* - — Connect a data provider
#[DataProvider('method')] - — External data provider
#[DataProviderExternal(Class::class, 'method')] - — Inline data provider
#[TestWith([args])] - — Custom TestDox description
#[TestDox('description')] - — Declare test dependency
#[Depends('testMethod')] - — Assign to group
#[Group('name')] - — Link to issue tracker
#[Ticket('PROJ-123')]
- — 将非
#[Test]方法标记为测试方法test* - — 关联数据提供器
#[DataProvider('method')] - — 外部数据提供器
#[DataProviderExternal(Class::class, 'method')] - — 内联数据提供器
#[TestWith([args])] - — 自定义TestDox描述
#[TestDox('description')] - — 声明测试依赖
#[Depends('testMethod')] - — 分配到测试组
#[Group('name')] - — 关联到问题追踪系统
#[Ticket('PROJ-123')]
Skip / Conditional
跳过/条件执行
- — Skip if PHP version doesn't match
#[RequiresPhp('>= 8.4')] - — Skip if extension missing
#[RequiresPhpExtension('pdo_pgsql')] - — Skip on other OS
#[RequiresOperatingSystemFamily('Linux')] - — Skip if function missing
#[RequiresFunction('sodium_crypto_sign')] - — Skip if method missing
#[RequiresMethod(PDO::class, 'sqliteCreateFunction')]
- — 如果PHP版本不匹配则跳过
#[RequiresPhp('>= 8.4')] - — 如果扩展缺失则跳过
#[RequiresPhpExtension('pdo_pgsql')] - — 在其他操作系统上跳过
#[RequiresOperatingSystemFamily('Linux')] - — 如果函数缺失则跳过
#[RequiresFunction('sodium_crypto_sign')] - — 如果方法缺失则跳过
#[RequiresMethod(PDO::class, 'sqliteCreateFunction')]
Coverage
覆盖率
- — Test covers this class
#[CoversClass(ClassName::class)] - — Test covers this function
#[CoversFunction('functionName')] - — Test covers this method
#[CoversMethod(ClassName::class, 'method')] - — Test contributes no coverage (integration tests)
#[CoversNothing] - — Allowed but not covered dependency
#[UsesClass(ClassName::class)] - — Allowed but not covered function
#[UsesFunction('functionName')]
- — 测试覆盖此类
#[CoversClass(ClassName::class)] - — 测试覆盖此函数
#[CoversFunction('functionName')] - — 测试覆盖此方法
#[CoversMethod(ClassName::class, 'method')] - — 测试不贡献覆盖率(集成测试)
#[CoversNothing] - — 允许但不覆盖的依赖
#[UsesClass(ClassName::class)] - — 允许但不覆盖的函数
#[UsesFunction('functionName')]
Fixture
夹具
- — Run method before each test (alternative to setUp)
#[Before] - — Run method after each test (alternative to tearDown)
#[After] - — Run static method before first test
#[BeforeClass] - — Run static method after last test
#[AfterClass] - — Backup/restore globals for this test
#[BackupGlobals(true)] - — Backup/restore static properties
#[BackupStaticProperties(true)]
- — 在每个测试前运行方法(替代setUp)
#[Before] - — 在每个测试后运行方法(替代tearDown)
#[After] - — 在第一个测试前运行静态方法
#[BeforeClass] - — 在最后一个测试后运行静态方法
#[AfterClass] - — 为此测试备份/恢复全局变量
#[BackupGlobals(true)] - — 为此测试备份/恢复静态属性
#[BackupStaticProperties(true)]
Test Behavior
测试行为
- — Suppress risky test warning
#[DoesNotPerformAssertions] - — Isolate in separate PHP process
#[RunInSeparateProcess] - — All tests in class run isolated
#[RunTestsInSeparateProcesses] - /
#[Small]/#[Medium]— Time limit enforcement (1s/10s/60s)#[Large]
- — 抑制风险测试警告
#[DoesNotPerformAssertions] - — 在独立PHP进程中隔离执行
#[RunInSeparateProcess] - — 类中所有测试均隔离执行
#[RunTestsInSeparateProcesses] - /
#[Small]/#[Medium]— 时间限制强制执行(1秒/10秒/60秒)#[Large]
Test Organization
测试组织
Directory Structure
目录结构
tests/
├── Unit/ # Fast, isolated, no I/O
│ ├── Service/
│ │ └── PaymentServiceTest.php
│ └── Model/
│ └── UserTest.php
├── Integration/ # Real dependencies, slower
│ └── Repository/
│ └── UserRepositoryTest.php
└── bootstrap.php # Autoloader for tests- Mirror source directory structure under and
tests/Unit/.tests/Integration/ - Unit tests — no database, no filesystem, no network. Mock all boundaries.
- Integration tests — real dependencies. Mark with to avoid polluting coverage metrics.
#[CoversNothing]
tests/
├── Unit/ # 快速、隔离、无I/O操作
│ ├── Service/
│ │ └── PaymentServiceTest.php
│ └── Model/
│ └── UserTest.php
├── Integration/ # 使用真实依赖,速度较慢
│ └── Repository/
│ └── UserRepositoryTest.php
└── bootstrap.php # 测试自动加载器- 在和
tests/Unit/下镜像源码目录结构。tests/Integration/ - 单元测试 — 无数据库、无文件系统、无网络。模拟所有边界。
- 集成测试 — 使用真实依赖。标记为以避免污染覆盖率指标。
#[CoversNothing]
Test Suites
测试套件
Define in for selective execution:
phpunit.xmlxml
<testsuites>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="integration">
<directory>tests/Integration</directory>
</testsuite>
</testsuites>Run subsets: , .
phpunit --testsuite unitphpunit --group slow在中定义以支持选择性执行:
phpunit.xmlxml
<testsuites>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="integration">
<directory>tests/Integration</directory>
</testsuite>
</testsuites>运行子集:、。
phpunit --testsuite unitphpunit --group slowConfiguration
配置
Recommended phpunit.xml
推荐的phpunit.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
cacheDirectory=".phpunit.cache"
executionOrder="depends,random"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
failOnWarning="true"
failOnRisky="true"
failOnDeprecation="true"
failOnNotice="true">
<testsuites>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="integration">
<directory>tests/Integration</directory>
</testsuite>
</testsuites>
<source restrictDeprecations="true"
restrictNotices="true"
restrictWarnings="true">
<include>
<directory suffix=".php">src</directory>
</include>
</source>
</phpunit>xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
cacheDirectory=".phpunit.cache"
executionOrder="depends,random"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
failOnWarning="true"
failOnRisky="true"
failOnDeprecation="true"
failOnNotice="true">
<testsuites>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="integration">
<directory>tests/Integration</directory>
</testsuite>
</testsuites>
<source restrictDeprecations="true"
restrictNotices="true"
restrictWarnings="true">
<include>
<directory suffix=".php">src</directory>
</include>
</source>
</phpunit>Key Configuration Choices
关键配置选项
- — randomize test order to catch hidden dependencies while respecting explicit
executionOrder="depends,random".#[Depends] - — flag tests without assertions as risky.
beStrictAboutTestsThatDoNotTestAnything="true" - — catch deprecations from your code early.
failOnDeprecation="true" - with
<source>— only surface issues from your code, not vendor dependencies.restrictDeprecations - — add
cacheDirectoryto.phpunit.cache..gitignore
- — 随机化测试顺序以发现隐藏依赖,同时尊重显式的
executionOrder="depends,random"声明。#[Depends] - — 将无断言的测试标记为风险测试。
beStrictAboutTestsThatDoNotTestAnything="true" - — 尽早捕获代码中的弃用警告。
failOnDeprecation="true" - 带的
restrictDeprecations— 仅显示自身代码的问题,而非依赖库的问题。<source> - — 将
cacheDirectory添加到.phpunit.cache。.gitignore
Code Coverage
代码覆盖率
Requires PCOV or Xdebug extension:
bash
phpunit --coverage-html build/coverage --coverage-clover build/clover.xmlUse and attributes to target coverage precisely. With
, tests without coverage attributes are risky.
#[CoversClass]#[UsesClass]beStrictAboutCoverageMetadata="true"See for the full XML reference, coverage report types, and execution
order options.
${CLAUDE_SKILL_DIR}/references/configuration.md需要PCOV或Xdebug扩展:
bash
phpunit --coverage-html build/coverage --coverage-clover build/clover.xml使用和属性精准定位覆盖率。当设置时,无覆盖率属性的测试会被标记为风险测试。
#[CoversClass]#[UsesClass]beStrictAboutCoverageMetadata="true"有关完整的XML参考、覆盖率报告类型和执行顺序选项,请参阅。
${CLAUDE_SKILL_DIR}/references/configuration.mdApplication
应用场景
When writing tests: apply all conventions silently — don't narrate each rule being followed. Match the project's
existing test style. If an existing codebase contradicts a convention, follow the codebase and flag the divergence once.
When reviewing tests: cite the specific issue and show the fix inline. Don't lecture — state what's wrong and how to
fix it.
Bad: "According to PHPUnit best practices, you should use createStub
instead of createMock when you don't need expectations..."
Good: "createMock → createStub (no expects() call, stub is sufficient)"当编写测试时:静默应用所有约定——无需说明每条规则。匹配项目现有的测试风格。如果现有代码库与约定冲突,请遵循代码库并一次性标记差异。
当评审测试时:指出具体问题并展示内联修复。不要说教——说明问题所在及修复方法。
错误示例: "根据PHPUnit最佳实践,当不需要预期时,应使用createStub
而非createMock..."
正确示例: "createMock → createStub(未调用expects(),桩件已足够)"Integration
集成
The php skill governs language choices; this skill governs PHPUnit testing decisions. The coding skill governs
workflow (discovery, planning, verification).
Test behavior, not implementation. When in doubt, mock less.
php技能管理语言选择;本技能管理PHPUnit测试决策。coding技能管理工作流(发现、规划、验证)。
测试行为,而非实现。如有疑问,减少模拟。