Mockery 处理 final 类与 final 方法:Proxy Mock 代理 Mock 原理与 Laravel 测试实战
Mockery 处理 final 类与 final 方法Proxy Mock 代理 Mock 原理与 Laravel 测试实战【免费下载链接】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在 PHP 单元测试中final关键字是 Mockery 这类 Mock 框架最头疼的限制之一它禁止子类覆盖方法而 Mockery 恰恰依赖生成子类来实现 Mock。本文基于本仓库 Laravel 示例samples/development-frameworks/laravel所携带的 Mockery 文档final_methods_classes.rst完整讲解 Mockery 应对 final 类与 final 方法的代理 MockProxy Mock方案何时可用、何时受限、底层是如何实现的以及如何在 Laravel 项目中安全落地。读完本文你将掌握面对final class/final method时最稳妥的 Mock 策略并能从源码与测试层面验证其行为边界。为什么final是 PHP Mock 框架的天敌Mockery 生成 Mock 对象的基本手段是子类化subclassing为被 Mock 的类生成一个子类并覆盖其中的方法从而拦截调用、注入预期行为expectations。这决定了两个硬约束被final修饰的类不能被继承因此无法生成子类来覆盖方法被final修饰的方法不能在子类中被重写因此即使类本身可继承该方法也无法被替换。final关键字的价值在于类型安全与防篡改但这与运行时替换行为的 Mock 需求天然冲突。因此Mockery 官方文档给出的最简单解决方案是不要将类或方法标记为final。但在类型安全与 Mock 能力之间必须妥协时Mockery 提供了Proxy Mock代理 Mock作为折中方案。Proxy Mock把实例对象传给\Mockery::mock()当目标类本身被标记为final或类中包含final方法时不能通过类名生成子类但可以传入一个已实例化的对象来创建代理 Mock// 传类名类为 final 时会抛异常详见下文源码分析 // $mock \Mockery::mock(SomeFinalClass); // 抛出 Mockery\Exception // 传实例对象生成 Proxy Mock一切照常工作 $mock \Mockery::mock(new SomeFinalClass); // 对代理 Mock 设置预期与普通 Mock 完全一致 $mock-shouldReceive(foo)-andReturn(baz); $result $mock-foo(); // baz其工作原理是Mockery 不再继承目标类而是生成一个Proxy代理指向你传入的真实对象对于设置了预期的方法调用代理拦截并返回预期结果对于未设置预期的方法调用代理将调用转发reroute给真实对象执行。关键代价代理 Mock 不继承被 Mock 对象的类类型即不会通过instanceof检查。若代码中存在类型约束typehint例如函数签名要求SomeFinalClass $obj传入代理 Mock 将失败。这正是文档中Mocking functionality 与 type safety 之间的 compromise妥协的含义。在 Laravel 测试中的典型用法在 Laravel 项目中当需要 Mock 一个来自第三方包、被final修饰的服务类时可以通过容器绑定实例$realService new FinalPaymentGateway($config); $proxy \Mockery::mock($realService); $proxy-shouldReceive(charge)-once()-andReturn(true); $this-app-instance(FinalPaymentGateway::class, $proxy); // 此时业务代码中的 $this-app-make(...) 会拿到代理 Mock需要注意的是若业务代码对FinalPaymentGateway有构造器类型约束constructor typehint代理 Mock 因不满足instanceof而无法注入——此时应优先考虑重构去掉final或引入接口而不是强行 Mock。源码级原理Mockery 如何拒绝与退让本仓库的 Mockery 源码library/Mockery清晰展示了这套策略的完整实现链。1. 类为final直接抛异常并提示代理方案在 MockConfiguration.php 的getTargetClass()中当按类名解析目标类时一旦发现其为final且没有传入实例对象立即抛出异常if ($this-getTargetObject() false $dtc-isFinal()) { throw new \Mockery\Exception( The class . $this-targetClassName . is marked final and its methods . cannot be replaced. Classes marked final can be passed in . to \Mockery::mock() as instantiated objects to create a . partial mock, but only if the mock is not subject to type . hinting checks. ); }注意异常信息中的三个要点① 按类名 Mockfinal类被明确拒绝② 补救方式是传入已实例化对象③ 前提是不涉及类型检查——即无法通过instanceof/typehint与文档结论完全一致。2. 生成阶段final类跳过extends在代码生成管线 ClassPass.php 中Pass 负责把生成的 Mock 代码从implements MockInterface改写为extends \TargetClass implements MockInterface但当目标类是final时直接短路返回不生成继承关系if ($target-isFinal()) { return $code; }这正是代理 Mock 无法通过instanceof检查的根因——生成的 Mock 类与目标类之间根本没有继承链。3. 方法筛选final方法被直接排除在 MockConfiguration.php 的getMethodsToMock()中所有候选方法先经过一轮过滤foreach ($methods as $key $method) { if ($method-isFinal()) { unset($methods[$key]); } }final方法不会进入MethodDefinitionPassMethodDefinitionPass.php的方法覆盖清单因此对含final方法的类类本身非final仍可通过类名 Mock只是final方法保持原样——这对应文档与源码中自动生成的内部 partialinternal partial场景。4. 实例对象如何进入 Mock 配置在 Container.php 的参数解析中当传入的参数是对象时它被当作 partial 目标加入 builder} elseif (is_object($arg)) { $partial array_shift($args); $builder-addTarget($partial); continue; }随后 MockConfiguration.php 的getTargetObject()返回该真实对象isInstanceMock()L400-L403标记其为实例 Mock并驱动 InstanceMockPass.php 在生成的类中注入构造逻辑从关联的真实对象拷贝属性、克隆其预期expectations实现代理转发的基础设施。5. 运行时未匹配预期的调用转发给真实对象在 Mock 基类 Mock.php 中保存真实对象的字段注释直接点明其职责/** * Instance of a core object on which methods are called in the event * it has been set, and an expectation for one of the objects methods * does not exist. This implements a simple partial mock proxy system. */ protected $_mockery_partial null;调用未命中任何预期时运行时分派逻辑会走_mockery_deferMissing/_mockery_ignoreMissing分支Mock.php对代理 Mock 而言未设置预期的调用被延后defer到真实对象执行从而让只 Mock 几个方法、其余照常的局部 Mock 语义成立。测试用例验证行为边界的实证仓库测试 ContainerTest.php 用一组针对性用例锁定了这些行为可直接作为行为契约阅读场景 Afinal类按类名 Mock 抛异常L260-L263配合测试夹具final class MockeryFoo3L1377-L1383public function testMockingAKnownConcreteFinalClassThrowsErrors_OnlyPartialMocksCanMockFinalElements() { $m $this-container-mock(MockeryFoo3); // 抛 \Mockery\Exception }场景 Bfinal类可通过传入实例创建代理 Mock且不通过instanceofL273-L279public function testFinalClassesCanBePartialMocks() { $m $this-container-mock(new MockeryFoo3); $m-shouldReceive(foo)-andReturn(baz); $this-assertEquals(baz, $m-foo()); $this-assertFalse($m instanceof MockeryFoo3); // 关键断言不是同一类型 }场景 C含final方法的类可用类名 Mock也可代理 MockL298-L322夹具为class MockeryFoo4含final public function foo()L1385-L1396// 代理 partial传入实例foo() 是 final 但仍可被 Mock因为代理不受 final 限制 $m $this-container-mock(new MockeryFoo4); $m-shouldReceive(foo)-andReturn(baz); $this-assertEquals(baz, $m-foo()); $this-assertEquals(bar, $m-bar()); // 未 Mock 的方法转发给真实对象 $this-assertTrue($m instanceof MockeryFoo4); // 类本身非 final仍满足 instanceof // 传统 partialMockeryFoo4[bar] 可 Mock bar() $m $this-container-mock(MockeryFoo4[bar]); // 但 MockeryFoo4[foo] 无法 partial 化 final 方法 // 期望被忽略foo() 仍返回真实实现 baz $m $this-container-mock(MockeryFoo4[foo]); $m-shouldReceive(foo)-andReturn(foo); $this-assertEquals(baz, $m-foo()); // final 方法不可覆盖断言返回真实值场景 C 揭示了三个层次的结论代理 Mock 能间接 Mockfinal方法因为代理类与目标类无关传统 partial 对final方法的期望会被静默忽略对应getMethodsToMock()的过滤逻辑类本身非final时即使走代理instanceof依然成立。场景 DSPL 内置类同样适用L281-L296SplFileInfo含 public final 方法仍可按类名 Mock 且满足instanceof SplFileInfo——这正是内部自动 partial策略含final方法的类final方法保持原样、其余方法可 Mock的典型代表。这也是 partial_mocks.rst 中第四种内部 partial所指的场景。与 Partial Mock 体系的关系本主题与 Mockery 的 Partial Mock 机制紧密相关详见仓库文档 partial_mocks.rst。Mockery 共提供四种 partial 策略其中三种面向用户策略用法是否受final影响instanceof 是否成立传统 Partial\Mockery::mock(MyClass[foo,bar])final方法无法被 partial 化成立类非 final 时被动 Partial\Mockery::mock(MyClass)-makePartial()同左成立代理 PartialProxy Mock\Mockery::mock(new MyClass)可绕过final限制不成立第四种为框架内部自动生成当你按类名 Mock 一个含final方法的类时Mockery 自动将这些方法排除在覆盖范围外形成内部 partial无需也无法手动指定。若确实必须 Mock 某个final方法代理 Mock 是唯一途径且必须接受instanceof失效的代价。工程实践建议与限制总结在 Laravel或其他 PHP 项目中使用 Mockery 面对final代码时建议按以下顺序决策首选移除final。若类/方法没有硬性安全需求去掉final是最简单、最无副作用的方案Mockery 文档亦明确推荐次选引入接口。将final类的公共行为抽象为接口让业务代码依赖接口而非具体类测试时 Mock 接口即可兼顾类型安全与可测性兜底代理 Mock。对无法修改的第三方final类使用\Mockery::mock($instance)创建代理但务必确认被测代码没有对该类的构造器/参数类型约束否则注入会失败避免不要指望对final方法的期望能被传统 partial 尊重——它会被静默忽略且不会报错容易造成测试看似通过、实际未生效的假象。适用前提与限制本文所述行为基于本仓库 vendor 目录中携带的 Mockery 版本samples/development-frameworks/laravel/vendor/mockery/mockery其composer.json定义了版本约束README 位于 README.md。核心结论——代理 Mock 可 Mockfinal类/方法但不满足instanceof——由源码 MockConfiguration.php 与测试 ContainerTest.php 共同保证是稳定且可验证的行为契约。【免费下载链接】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创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

一文搞懂重心的性质

一文搞懂重心的性质

3步搞定重心性质入门到精通 告别报错Stack Trace 昨晚跑代码,满屏红色的 Stack Trace 像天书一样糊脸,盯着 NullPointerException 或 IndexOutOfBounds…

2026/9/23 16:26:24 阅读更多 →
用户画像生成系统源码解析:从数据清洗到聚类分群的完整流水线

用户画像生成系统源码解析:从数据清洗到聚类分群的完整流水线

简介:一份基于Python构建用户画像生成系统的完整源码,面向数据分析、产品运营及后端开发人员,解决用户理解与精细化运营难题,演示从用户行为数据采集清洗、特征工程、行为分析、聚类分群到画像构建与可视化的全流程实现。资源共14…

2026/9/23 16:25:24 阅读更多 →
BP三维雷达成像:从后向投影原理到Python仿真全流程

BP三维雷达成像:从后向投影原理到Python仿真全流程

简介:基于反投影算法的三维雷达点目标成像MATLAB脚本,专门面向机载雷达下视成像场景,适用于雷达信号处理初学者或需要快速验证成像算法的研究人员;机载下视成像常用于地形测绘、目标检测与跟踪,三维结果可提供目标位置…

2026/9/23 16:25:23 阅读更多 →

最新新闻

ZK框架前端技术解析:ZUL、zhtml与native组件差异与选型

ZK框架前端技术解析:ZUL、zhtml与native组件差异与选型

1. 三者到底是什么:概念拆解与定位1.1 ZUL:ZK框架的"骨架语言"先说结论:ZUL是ZK框架定义的一种XML风格的UI描述语言。你在ZUL文件里写的每一个标签,最终都会映射到Java后端的一个组件类实例。举个最直接的例子&#xff…

2026/9/23 17:47:04 阅读更多 →
SSM框架下的软件工程项目管理系统:从部署到论文答辩全攻略

SSM框架下的软件工程项目管理系统:从部署到论文答辩全攻略

简介:一套基于JavaSSMMySQL的软件工程项目管理系统毕业设计成果,面向高校计算机相关专业学生,尤其适合作为毕业设计、课程设计或期末大作业的完整参考。项目已通过导师指导并获高分评价,前后端代码、数据库脚本及配套论文一次打包…

2026/9/23 17:47:04 阅读更多 →
智器q5入门到精通:3个致命坑让你少走弯路

智器q5入门到精通:3个致命坑让你少走弯路

智器q5入门到精通:3个致命坑让你少走弯路 盯着屏幕满屏红色的 StackTrace,心里慌得一批?别急,这场景我太熟悉了。 很多刚接触 智器q5 开发的朋友,一上来就对着报错信息发呆,根本看不出哪行代码出了岔子。想从 入门到精通…

2026/9/23 17:47:04 阅读更多 →
Relay Store 编程式数据更新完全指南:RecordSourceSelectorProxy、RecordProxy 与 ConnectionHandler 深度解析

Relay Store 编程式数据更新完全指南:RecordSourceSelectorProxy、RecordProxy 与 ConnectionHandler 深度解析

前端开发工具 【免费下载链接】relay Relay is a JavaScript framework for building data-driven React applications. 项目地址: https://gitcode.com/gh_mirrors/relay29/relay 点击查看 免费下载 本文是 Relay(relay-runtime)Store API …

2026/9/23 17:47:04 阅读更多 →
真野猪套面试必问:3个核心坑点让你一次过

真野猪套面试必问:3个核心坑点让你一次过

真野猪套面试必问:3个核心坑点让你一次过 版本升级后 API 全变了,真野猪套相关的底层逻辑也没变,但封装层彻底重构。 很多老手在面试真野猪套进阶用法时,卡在接口兼容性上,导致答非所问。…

2026/9/23 17:47:04 阅读更多 →
4通道独立称重配料控制系统:基于CB4与Modbus RTU的实战

4通道独立称重配料控制系统:基于CB4与Modbus RTU的实战

做配料和配水这行的朋友应该都有体会:配料精度直接决定成品质量,也直接决定成本。某一个组分差个十几克,整批料可能就废掉了,而现场的称重信号飘、通信掉线、继电器打火干扰这些毛病,又是做控制系统最头疼的事。我这次…

2026/9/23 17:46:03 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →