如何使用WP_Mock和Mockery模拟$wpdb->prefix属性?
环境信息
| 工具/框架 | 版本 |
|---|---|
| PHP | 7.4 |
| WordPress | 6.2 |
| WP_Mock | 1.0 |
| PHPUnit | 9.6.11 |
| Mockery | 1.6.6 |
问题重现
测试用例代码
use PHPUnit\Framework\TestCase; use WP_Mock; use Mockery; class WPAB_Database_Service_Test extends TestCase { /** * @test */ public function migrate() { // ... 其他初始化代码 ... $wpdb_mock = Mockery::mock('WPDB'); $wpdb_mock ->allows('get_charset_collate') ->withNoArgs() ->andReturn('utf16_general_ci'); $wpdb_mock->prefix = 'wpbb_'; $result = $database_service->migrate(); // 待测试方法 $this->assertSame($result, 1); } }
待测试代码中的问题行
$prefix = $wpdb->prefix;
问题现象
运行测试时抛出警告:
PHP Notice: Undefined property: Mockery_1__WPDB::$prefix in ...
测试意外通过,推测Mockery对未模拟的属性默认返回空字符串。
解决方案:模拟属性的正确方式
一、Mockery 模拟非静态属性
直接给Mock对象赋值属性会因为Mockery的代理机制失效,必须通过Mockery的API来声明属性访问:
方式1:宽松模拟(仅返回值,不验证访问次数)
$wpdb_mock = Mockery::mock('WPDB'); // 模拟prefix属性的读取 $wpdb_mock->allows('prefix')->andReturn('wpbb_'); // 保留原有方法的模拟 $wpdb_mock ->allows('get_charset_collate') ->withNoArgs() ->andReturn('utf16_general_ci');
方式2:严格模拟(验证属性是否被访问)
如果需要确保属性被正确读取,可以用shouldReceive添加访问次数断言:
$wpdb_mock = Mockery::mock('WPDB'); // 断言prefix属性被读取1次 $wpdb_mock->shouldReceive('prefix')->once()->andReturn('wpbb_');
二、Mockery 模拟静态属性
针对类的静态属性,需要用mockStatic方法处理:
// 模拟WPDB类的静态属性$static_prefix Mockery::mockStatic('WPDB') ->shouldReceive('static_prefix') ->andReturn('wp_static_');
三、结合WP_Mock 模拟WordPress全局对象
WP_Mock专为WordPress生态设计,模拟全局$wpdb这类对象更贴合场景:
public function setUp(): void { WP_Mock::setUp(); // 模拟全局$wpdb对象及其属性、方法 WP_Mock::userFunction('get_global', [ 'args' => ['wpdb'], 'return' => (object) [ 'prefix' => 'wpbb_', 'get_charset_collate' => fn() => 'utf16_general_ci' ] ]); } public function tearDown(): void { WP_Mock::tearDown(); Mockery::close(); } // 测试方法中直接使用全局$wpdb即可 public function migrate() { // ... 初始化$database_service ... $result = $database_service->migrate(); $this->assertSame($result, 1); }
四、避免警告的最佳实践
- 禁止直接给Mock对象赋值属性:必须通过Mockery/WP_Mock的API声明属性访问逻辑,避免触发未定义属性警告。
- 优先用WP_Mock处理WordPress全局对象:WP_Mock提供的API更适配WordPress的全局变量、函数体系,减少Mockery的使用复杂度。
- 启用Mockery严格模式:确保所有被访问的属性/方法都被提前模拟,避免测试意外通过:
$wpdb_mock = Mockery::mock('WPDB')->shouldBeStrict();
内容的提问来源于stack exchange,提问作者4uruanna
相关产品推荐
相关产品推荐

