尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

使用 Mockery 检测 Mock 对象:基于 `MockInterface` 的类型判断实战指南

使用 Mockery 检测 Mock 对象:基于 `MockInterface` 的类型判断实战指南 示例工程数据库教程后端【免费下载链接】sql-server-samplesAzure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge项目地址https://gitcode.com/gh_mirrors/sq/sql-server-samples点击查看免费下载导读本文围绕 Mockery 测试框架中的一个实用技巧展开如何可靠地判断一个 PHP 对象究竟是真实业务对象还是测试中生成的模拟Mock对象。核心答案是 Mockery 所有 mock 都统一实现\Mockery\MockInterface接口因此一次instanceof类型检查即可完成判别。文章将结合当前仓库中 Mockery 0.9.x 的源码实现、测试用例与 Laravel 集成方式说明这一机制的原理、适用场景与注意事项帮助你在调试复杂测试、编写通用测试工具时快速识别 mock 对象。说明本文讨论的 Mockery 代码来自仓库 samples/development-frameworks/laravel/vendor/mockery/mockery对应版本为mockery/mockery: 0.9.*见 composer.json 第 13 行是 Laravel 示例应用中的测试依赖。一、为什么需要“检测 Mock 对象”在单元测试中依赖注入的协作者对象经常被替换为 mock例如$service Mockery::mock(App\Services\PaymentGateway);此时测试代码拿到的$service是一个模拟对象而不是真实的App\Services\PaymentGateway实例。两者在行为上差异巨大mock 不会执行真实逻辑、不会真正访问数据库或网络只按期望expectation返回预设值。在实际开发中你可能会遇到以下场景需要区分二者调试辅助在复杂测试中打印对象状态时想知道某个变量到底是不是 mock避免被 mock 的返回值误导通用工具函数编写一个供多个测试复用的辅助函数需要根据入参是真实对象还是 mock 走不同分支断言增强自定义断言或异常信息中需要提示用户此处传入的是 mock 对象框架/中间件适配在自定义的依赖注入容器、事件分发器等组件中需要识别传入对象是否可安全调用真实方法。由于 mock 通常运行在测试环境、与真实对象混用一个低成本、零侵入的判别手段就显得很有价值。Mockery 为此提供了统一约定所有 mock 对象都实现\Mockery\MockInterface接口。二、核心机制一切 mock 都实现MockInterface原文档给出的判别方法非常简洁一次类型检查即可assert($mightBeMocked instanceof \Mockery\MockInterface);assert()会因传入false而抛出异常从而在测试中直接暴露当前对象是 mock这一事实如果你只需要布尔判断例如用于分支选择则直接使用instanceof的返回值即可if ($object instanceof \Mockery\MockInterface) { // 这是一个 Mockery mock } else { // 这是真实对象 }这一约定之所以成立源于 Mockery 的三层源码保证接口定义\Mockery\MockInterface在 library/Mockery/MockInterface.php 中定义声明了shouldReceive()、shouldNotReceive()、shouldIgnoreMissing()、shouldDeferMissing()、makePartial()、mockery_verify()等 mock 生命周期与期望管理方法基类实现Mockery 的 mock 运行时基类 library/Mockery/Mock.php 直接声明class Mock implements MockInterface将接口契约落实为具体实现代码生成保证Mockery 通过字符串代码生成器动态生成每个 mock 类。在 Generator/StringManipulation/Pass/ClassPass.php 中生成器会把占位符implements MockInterface替换为extends \目标类名 implements MockInterface同理InterfacePass.php 负责在 mock 接口时追加implements MockInterface。从源码结构可以推断无论被 mock 的是已存在的类、不存在的类、接口还是抽象类生成的 mock 类都会同时实现MockInterface因此类型判断对所有 mock 形态一致有效。三、测试用例印证四种 mock 形态均可识别仓库自带的单元测试验证了该机制对不同目标的覆盖。在 tests/Mockery/AdhocTest.php 中连续四个用例分别对已存在类、不存在的类、接口、抽象类执行 mock 创建并断言$m $this-container-mock(SplFileInfo); $this-assertTrue($m instanceof \Mockery\MockInterface);$m $this-container-mock(ABC_IDontExist); $this-assertTrue($m instanceof \Mockery\MockInterface);$m $this-container-mock(MockeryTest_NameOfInterface); $this-assertTrue($m instanceof \Mockery\MockInterface);$m $this-container-mock(MockeryTest_NameOfAbstract); $this-assertTrue($m instanceof \Mockery\MockInterface);由此可见类型检查不依赖于目标类是否真实存在也不受类 / 接口 / 抽象类的形态影响。此外tests/Mockery/ContainerTest.php、tests/Mockery/ExpectationTest.php 等测试也大量使用assertInstanceOf(Mockery\MockInterface, ...)作为断言依据进一步印证该接口是 Mockery 对外承诺的公共契约。四、实战在 Laravel 测试中落地检测当前仓库中的 Laravel 示例应用位于 samples/development-frameworks/laravel通过composer.json引入mockery/mockery: 0.9.*作为 dev 依赖。在基于 PHPUnit Mockery 的 Laravel 测试中可以像下面这样封装一个可复用的判别函数放在tests/下的公共 trait 或辅助类中namespace Tests; use Mockery\MockInterface; trait DetectsMocks { /** * 判断给定对象是否为 Mockery mock。 * * param mixed $object * return bool */ protected function isMock($object) { return $object instanceof MockInterface; } }在具体测试中结合断言使用public function testSomething() { $gateway Mockery::mock(App\Services\PaymentGateway); $this-assertTrue($this-isMock($gateway)); // 预期是 mock $real app(App\Services\PaymentGateway); $this-assertFalse($this-isMock($real)); // 真实对象不是 mock }几点实战建议只对 Mockery 的 mock 有效MockInterface是 Mockery 自家的契约用 PHPUnit 原生createMock()或 Prophecy 生成的 mock 不实现该接口无法用此法识别判别的边界makePartial()部分 mock、shouldDeferMissing()延迟代理等行为贴近真实对象的变体本质上仍是 Mockery mock同样会通过该检查——此时请勿仅凭instanceof结果断定对象不执行真实逻辑配合异常信息在通用断言库中检测到 mock 却需要真实对象时可以抛出包含Expected a real object, got a Mockery mock的异常帮助定位测试配置错误与容器解耦该方法不依赖任何容器或全局状态可在任意测试用例、辅助函数中直接使用。五、总结检测 Mock 对象的核心方法论可以浓缩为一条规则Mockery 中不存在无法辨认的 mock——所有 mock 都实现\Mockery\MockInterface一次instanceof即可完成判别。该约定由接口定义、基类实现与代码生成器三重保证并有覆盖类/接口/抽象类/不存在类的完整测试佐证见 MockInterface.php、Mock.php、AdhocTest.php。在实际的 Laravel/PHPUnit 测试工程中建议将判别逻辑封装为公共辅助函数并始终牢记其适用范围仅限于 Mockery 生成的 mock 对象。赞分享示例工程数据库教程后端【免费下载链接】sql-server-samplesAzure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge项目地址https://gitcode.com/gh_mirrors/sq/sql-server-samples点击查看免费下载相关推荐使用 Mockery 检测 Mock 对象基于 MockInterface 的类型判断实战指南使用 Mockery 检测 Mock 对象基于 MockInterface 的类型判断实战指南 导读 在 PHP 单元测试中我们经常需要在公共方法、辅助工具测试开发工具Valkey 集群部署实战一条命令搭建 6 节点分布式缓存集群Valkey 集群部署实战一条命令搭建 6 节点分布式缓存集群 Valkey 官方仓库中的 utils/create cluster https://linkKV存储缓存数据库Mockery 入门指南PHP 单元测试中的 Mock 对象框架实战手册Mockery 入门指南PHP 单元测试中的 Mock 对象框架实战手册 Mockery 是一个简洁而灵活的 PHP mock 对象框架专为 PHPUnit测试开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表