Test Guard — PHP / PHPUnit / Pest 模式
九条规则在 PHP 项目中的具体应用,包括 WordPress 和 WooCommerce。在审查或编写 PHP 测试时阅读此文件。
规则 2:PHP 中的 mock 边界
有理由的 mock 目标:
- HTTP:Guzzle 处理器/中间件、WordPress 中的
pre_http_request过滤器 - 外部 SDK:支付网关、邮件提供商、LLM API 客户端
- 时钟:注入时钟(
psr/clock)而不是直接调用time() - 外部路径上的文件系统(优先使用
vfsStream或临时目录而不是 mock)
无理由的 mock:
- 用 Mockery/Prophecy 替身模拟项目自己的值对象、DTO 或实体——请构造真实实例(规则 8)
- 仅为了隔离类而模拟内部服务——如果装配很痛苦,修复构造函数,不要伪造协作者
- 被测类的部分 mock——你不再测试该类本身
规则 3:数据提供者
/** * @see Rule 3 — variants of one scenario belong in a data provider. */#[DataProvider('provideSlugCases')]publicfunctiontest_slugify_normalizes_input(string$raw,string$expected):void{$this->assertSame($expected,slugify($raw));}publicstaticfunctionprovideSlugCases():array{returnarray('lowercases'=>array('Hello World','hello-world'),'strips padding'=>array(' padded ','padded'),'transliterates'=>array('Café Menu','cafe-menu'),);}Pest 等价写法:it('normalizes slug', ...)->with([...])。
WordPress 特有的边界
- 集成测试(
WP_UnitTestCase/wp-env/wp-cli scaffold):使用带工厂的真实 WordPress 测试框架——self::factory()->post->create()、self::factory()->user->create()。不要 mockWP_Post或WP_User;工厂的存在正是为了让你不必 mock(规则 8)。 - 未加载 WordPress 的单元测试(Brain Monkey / WP_Mock):模拟
get_option()或apply_filters()等 WordPress 函数是边界 mock,有理由。但要断言你的代码用这些值做了什么,而不是get_option被以特定参数调用(规则 1)。 - 使用
pre_http_request过滤器 mock 出站 HTTP,而不是修补wp_remote_get内部。 - 不要测试 WordPress 会净化、转义或钩子会触发——那是核心的保证(规则 7)。测试你的回调在给定输入时的行为。
WooCommerce 说明
- 在集成测试中通过
WC_Helper_Product和WC_Helper_Order构建真实的WC_Product/WC_Order对象——绝不使用它们的MagicMock风格替身(规则 8)。 - 购物车和结账逻辑是有状态的:优先使用集成测试而非重度 mock 的单元测试;mock 的购物车会隐藏钩子排序 bug。
规则 9:真实数据库
WP_UnitTestCase已经将每个测试包裹在针对真实模式的事务中——将其用于查询、元数据和持久化逻辑,而不是 mock$wpdb。Mock$wpdb->prepare或$wpdb->get_results来测试查询构建器什么都测不到。