PHP 测试规范实战指南:基于 ECC 规则体系的 PHPUnit 与 Pest 测试方法论

发布时间:2026/9/10 12:26:57
PHP 测试规范实战指南:基于 ECC 规则体系的 PHPUnit 与 Pest 测试方法论 PHP 测试规范实战指南基于 ECC 规则体系的 PHPUnit 与 Pest 测试方法论【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC本指南以 ECC 仓库中 rules/php/testing.md及其日文版 docs/ja-JP/rules/php/testing.md为核心骨架结合仓库内通用的 rules/common/testing.md、skills/laravel-tdd/SKILL.md、skills/tdd-workflow/SKILL.md 等源码级资料深度展开。面向 PHP / Laravel 开发者讲清楚测试框架选型、覆盖率治理、测试分层组织、Inertia 组件断言以及 RED → GREEN → REFACTOR 全流程读完即可在真实项目中落地执行。一、规则文件定位PHP 专属测试规范的适用范围rules/php/testing.md是 ECC 规则体系中针对 PHP 语言的测试子规范其文件头部的paths前置元数据明确声明了该规范生效的文件范围paths: - **/*.php - **/phpunit.xml - **/phpunit.xml.dist - **/composer.json也就是说凡是涉及.php源码、PHPUnit 配置文件phpunit.xml与phpunit.xml.dist以及composer.json用于声明测试脚本与 dev 依赖的改动都应遵循本规范。该文件并非孤立存在它通过一句扩展声明与通用测试规范挂钩This file extends common/testing.md with PHP specific content.本文件以 PHP 特有内容扩展通用测试规范。因此完整理解 PHP 测试规范必须先掌握其底座 rules/common/testing.md 中定义的全局硬性要求最低覆盖率 80%且单元测试、集成测试、E2E 测试三类测试全部必需TDD 为强制工作流先写测试RED→ 运行测试确认失败 → 写最小实现GREEN→ 运行测试确认通过 → 重构IMPROVE→ 校验覆盖率80%测试结构优先采用 AAAArrange-Act-Assert模式并使用能解释被测行为behavior under test的描述性命名例如returns empty array when no markets match query。下面各节将逐条展开 PHP 规范中的五大核心主题并结合仓库源码给出可执行细节。二、测试框架选型PHPUnit 为默认Pest 一旦启用便保持一致规则原文对框架的选择给出了明确且务实的取向UsePHPUnitas the default test framework. IfPestis configured in the project, prefer Pest for new tests and avoid mixing frameworks.翻译成落地准则就是两条默认框架是 PHPUnit无论新建项目还是存量项目没有特殊理由都应使用 PHPUnit 作为测试基础设施如果项目已经配置了 Pest那么新增测试优先使用 Pest并且严禁在同一项目中混用两套框架——混用会导致断言风格、测试文件组织、覆盖率统计口径的割裂。这一取向在配套技能 skills/laravel-tdd/SKILL.md 中有完整的呼应该技能同时覆盖 PHPUnit 与 Pest 两种写法并给出 Pest 特性测试的典型形态uses()引入RefreshDatabase、beforeEach统一登录、it(...)描述行为use App\Models\Product; use App\Models\User; uses(\Illuminate\Foundation\Testing\RefreshDatabase::class); beforeEach(function () { $this-user User::factory()-create(); $this-actingAs($this-user); }); it(lists products, function () { Product::factory()-count(3)-create([user_id $this-user-id]); $this-get(route(products.index)) -assertOk() -assertViewHas(products); });规则强调的“避免混用”本质上是让整个团队对“如何写测试”只有一套心智模型。如果项目从 PHPUnit 迁移到 Pest应整体切换并保持新老测试文件风格统一而不是按文件各写各的。三、覆盖率治理命令、驱动选择与 CI 阈值3.1 两条覆盖率命令规则给出了 PHP 项目生成覆盖率报告的两条标准命令vendor/bin/phpunit --coverage-text # 或 vendor/bin/pest --coveragevendor/bin/phpunit --coverage-text以纯文本形式输出 PHPUnit 的覆盖率汇总适合本地快速自查也适合在 CI 日志中直接展示vendor/bin/pest --coveragePest 内置覆盖率输出。针对 CI 场景skills/laravel-tdd/SKILL.md 进一步补充了两条更精细的命令# PHPUnit使用 clover 输出以便 CI 阈值检查 vendor/bin/phpunit --coverage-html coverage --coverage-clover clover.xml # Pest内置阈值支持 vendor/bin/pest --coverage --min80其中--coverage-clover clover.xml生成机器可读的 Clover 格式便于 CI 流水线解析并强制阈值vendor/bin/pest --coverage --min80则直接让 Pest 在覆盖率低于 80% 时以非零退出码失败把“覆盖率达标”变成构建的硬性门禁。3.2 CI 中优先 pcov 或 Xdebug规则明确指出PreferpcovorXdebugin CI, and keep coverage thresholds in CI rather than as tribal knowledge.两点关键要求覆盖率驱动选型CI 环境应使用pcov推荐性能更好或Xdebug来采集覆盖率而不是依赖本地的默认配置阈值由 CI 管理而非口头约定80% 这类阈值必须写进 CI 配置或phpunit.xml/pest.php形成可执行的强制检查杜绝“凭经验觉得覆盖率够了”的部落知识tribal knowledge。3.3 分层覆盖率目标skills/laravel-tdd/SKILL.md 给出了比“整体 80%”更细的组件级目标适合作为制定项目内部阈值的起点组件目标Models95%Actions/Services90%Form Requests90%Controllers85%Policies95%Overall80%可以看到规则体系对“纯逻辑层”模型、服务、策略的覆盖要求显著高于传输层控制器这与第五节“测试分层”的思想一脉相承业务规则越集中的地方越需要高覆盖率兜底。3.4 与静态分析诊断联动覆盖率不是唯一的质量信号。agents/php-reviewer.md 中定义的 PHP 代码评审 Agent 将覆盖率命令与静态分析工具整合为标准的诊断命令集./vendor/bin/phpstan analyse --level max # 类型安全与错误 ./vendor/bin/psalm --show-infotrue # 静态分析 ./vendor/bin/pint --test # PSR-12 格式检查 ./vendor/bin/phpunit --coverage-text # 测试覆盖率 composer audit # 依赖漏洞评审 Agent 的通过标准是“所有自动化检查通过且无 CRITICAL / HIGH 级问题”这为 PHP 测试规范提供了可操作的验收闭环。四、测试组织单元/集成分层、工厂化夹具、服务层下沉规则对测试组织给出了三条结构性要求这是全篇最核心的工程方法论4.1 分离快速单元测试与框架/数据库集成测试Separate fast unit tests from framework/database integration tests.单元测试聚焦单个函数、工具类、组件逻辑不触碰数据库与 HTTP 栈保证毫秒级执行速度集成测试覆盖 API 端点、数据库操作、服务交互允许拉起框架与真实/内存数据库。skills/laravel-tdd/SKILL.md 给出的 PHPUnit 配置用两个独立testsuite落实了这一分层phpunit xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:noNamespaceSchemaLocationvendor/phpunit/phpunit/phpunit.xsd bootstrapvendor/autoload.php colorstrue testsuites testsuite nameUnit directory suffixTest.phptests/Unit/directory /testsuite testsuite nameFeature directory suffixTest.phptests/Feature/directory /testsuite /testsuites php env nameAPP_ENV valuetesting/ env nameBCRYPT_ROUNDS value4/ env nameCACHE_STORE valuearray/ env nameDB_CONNECTION valuesqlite/ env nameDB_DATABASE value:memory:/ env nameMAIL_MAILER valuearray/ env nameQUEUE_CONNECTION valuesync/ env nameSESSION_DRIVER valuearray/ /php /phpunit该配置中tests/Unit与tests/Feature物理隔离同时用DB_CONNECTIONsqliteDB_DATABASE:memory:让集成测试跑在内存数据库上MAIL_MAILERarray、QUEUE_CONNECTIONsync、CACHE_STOREarray则将邮件、队列、缓存全部替换为内存假件保证测试快速、确定、可重复。4.2 用工厂/构造器代替手写大数组Use factory/builders for fixtures instead of large hand-written arrays.测试夹具不应是散落各处、难以维护的手写关联数组而应使用模型工厂factory或构造器builder统一生产。skills/laravel-tdd/SKILL.md 提供了标准工厂实现示例// database/factories/UserFactory.php class UserFactory extends Factory { protected static ?string $password null; public function definition(): array { return [ name fake()-name(), email fake()-unique()-safeEmail(), email_verified_at now(), password static::$password ?? Hash::make(password), remember_token Str::random(10), role user, ]; } public function admin(): static { return $this-state(fn (array $attributes) [role admin]); } }工厂的典型用法覆盖单条、批量、不落库、关联、序列等多种场景$user User::factory()-create(); $admin User::factory()-admin()-create(); $product Product::factory()-create([user_id $user-id]); $products Product::factory()-count(10)-create(); $draft Product::factory()-make(); // 不落库 // 关联关系 $user User::factory()-has(Product::factory()-count(3))-create(); // 序列 User::factory()-count(3)-sequence( [role admin], [role editor], [role user], )-create();工厂的价值在于默认值集中定义、状态state可组合、字段可覆盖测试可读性远高于散落的手写数组。4.3 HTTP/控制器测试只管传输与校验业务规则下沉到服务层Keep HTTP/controller tests focused on transport and validation; move business rules into service-level tests.这是与 rules/php/patterns.md 中“瘦控制器、显式服务Thin Controllers, Explicit Services”模式直接对应的测试组织原则控制器只负责传输认证、参数校验、序列化、状态码业务规则放进应用/领域服务使其可在不启动 HTTP 的情况下被测试。因此测试也应遵循同一边界HTTP/控制器测试断言“请求被正确接收、参数被正确校验、响应形态正确”服务层测试才去断言业务规则价格计算、库存扣减、权限判定等。典型控制器测试示例来自 skills/laravel-tdd/SKILL.mdpublic function test_it_validates_required_fields(): void { $this-actingAs(User::factory()-create()); $this-post(route(products.store), []) -assertSessionHasErrors([name, price]); } public function test_users_cannot_modify_others_products(): void { $owner User::factory()-create(); $attacker User::factory()-create(); $product Product::factory()-create([user_id $owner-id]); $this-actingAs($attacker) -delete(route(products.destroy, $product)) -assertForbidden(); }注意两个测试都只验证“传输与校验”层面的行为字段缺失导致校验错误、越权操作被 403 拒绝而没有把折扣计算、库存逻辑等业务规则塞进控制器测试。五、Inertia.js 项目用 assertInertia 验证组件与 Props规则针对使用 Inertia.js 的项目给出了一条专门的测试要求If the project uses Inertia.js, preferassertInertiawithAssertableInertiato verify component names and props instead of raw JSON assertions.含义是当控制器通过 Inertia.js 渲染页面时测试不应把响应当作普通 JSON 去做脆弱的字符串/键值断言而应使用 Laravel 内置的assertInertia配合AssertableInertia类型化断言直接验证渲染的组件名component name传递给组件的 props 结构包括嵌套断言、缺失键、类型校验。例如对于一个返回Inertia::render(Products/Index, [...])的控制器理想测试形态如下结合规则要求给出的示意写法$this-actingAs($user) -get(route(products.index)) -assertInertia(fn (Assert $page) $page -component(Products/Index) -has(products, 5) -where(user.name, $user-name));相比手写assertJson解析原始响应AssertableInertia的component()、has()、where()、missing()等断言更贴合 Inertia 的页面契约组件改名或 props 结构调整时能立刻暴露问题而不是让测试在 JSON 迷宫里“静默通过”。六、TDD 全流程RED → GREEN → REFACTOR 与 Git 检查点PHP 测试规范在结尾将读者指向 skills/tdd-workflow/SKILL.md这是仓库全局 TDD 循环的完整实现。其核心循环在 PHP 场景下落地为// Step 1: RED —— 先写一个会失败的测试 public function test_a_product_can_be_created(): void { $product Product::factory()-create([name Test Product]); $this-assertDatabaseHas(products, [name Test Product]); } // Step 2: GREEN —— 编写 migration、模型与工厂让测试通过 // Step 3: REFACTOR —— 保持测试全绿的前提下改进代码示例出自 skills/laravel-tdd/SKILL.md6.1 RED 门禁的强制语义技能文档对 RED 状态有严格定义值得完整引述相关测试目标必须成功编译且新测试确实被执行结果为 RED或者通过编译期错误本身作为 RED 信号失败原因必须是“预期的业务逻辑 bug / 未定义行为 / 缺失实现”而不是无关的语法错误、测试环境损坏或缺失依赖只写了但从未编译执行过的测试不构成 RED。在确认 RED 之前禁止修改任何生产代码。6.2 Git 检查点提交技能要求在每个 TDD 阶段之后创建检查点提交checkpoint commit推荐的最小化工作流是一个提交新增失败测试并验证 RED —— 建议消息test: add reproducer for feature or bug一个提交最小修复并验证 GREEN —— 建议消息fix: feature or bug一个可选提交重构完成 —— 建议消息refactor: clean up after feature or bug implementation。检查点提交必须位于当前活动分支、从当前HEAD可达才算有效证据若后续会 squash 合并需将 RED/GREEN/重构摘要复制到 PR 描述或 squash 提交信息中确保评审者仍能看出“验证了什么、如何验证的”。6.3 TDD 证据报告GREEN 与覆盖率验证通过后技能要求编写一份人类可读的证据报告建议路径如docs/testing/plan-or-task-name.tdd.md、.github/tdd/或.claude/tdd/内容包含来源计划、用户旅程、每个任务执行的验证命令与输出摘录含 RED/GREEN 结果、测试规格表保证什么、哪个测试文件、什么类型、结果、证据、覆盖率与已知缺口。报告必须如实引用实际命令与结果禁止编造未运行的 PASS 记录。七、配套技能与 Agent 生态把规则变成可执行的闭环PHP 测试规范并不是孤立的文字约束它在 ECC 仓库中与技能、Agent 组成完整执行链skills/laravel-tdd/SKILL.mdLaravel 场景下的 PHPUnit/Pest 测试全模式包括模型工厂、特性/HTTP 测试、JSON API 测试、Sanctum 认证测试、以及Http::fake()/Mail::fake()/Notification::fake()/Queue::fake()/Storage::fake()/Event::fake()六类假件用法还有 Artisan 命令测试与授权测试模板skills/tdd-workflow/SKILL.md仓库级 RED → GREEN → REFACTOR 循环、测试运行器探测、覆盖率阈值与证据报告规范agents/php-reviewer.mdPHP 评审 Agent 在执行git diff -- *.php后用 PHPStan、Psalm、Pint、PHPUnit 覆盖率与composer audit做自动化检查并按 CRITICAL / HIGH / MEDIUM 分级输出评审结论rules/common/testing.md为所有语言定义的 80% 覆盖率底线、AAA 测试结构与描述性命名rules/php/patterns.md提供“瘦控制器 显式服务 DTO/值对象 依赖注入”等与测试分层互为表里的架构约束。这五层相互咬合规则定标准技能给方法Agent 做检查共同构成一套从“写测试”到“验收测试质量”的完整闭环。对于 PHP / Laravel 团队而言按本指南落地即可把“测试框架选型、覆盖率治理、分层组织、Inertia 断言、TDD 循环”全部固化为可执行、可度量、可评审的工程实践。【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考