ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Mockery 全局配置指南:掌控不存在方法 Mock、未使用期望与内部类参数映射

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点击查看免费下载本文基于 sql-server-samples 仓库中 Laravel 开发框架示例项目samples/development-frameworks/laravel所携带的 Mockery 测试框架文档深入讲解 Mockery 的全局配置对象及其三大核心行为。读者将掌握通过\Mockery::getConfiguration()精细调控是否允许 Mock 不存在的方法、是否允许未被满足的期望以及为 PHP 内部类方法手工声明参数映射三项能力并结合仓库源码与测试用例理解其底层生效机制从而在 PHPUnit 测试套件中写出更严谨、更可维护的 Mock 测试。Mockery 全局配置一个单例三项开关Mockery 允许对部分核心行为进行细粒度微调其实现方式是一个单例配置对象singleton configuration object。在仓库携带的 Mockery 源码中这个对象就是 library/Mockery/Configuration.php 中定义的Mockery\Configuration类它持有当前配置的三项核心行为是否允许 Mock 实际并不存在的方法默认开启是否允许存在从未被满足即未被使用的期望默认开启为 PHP 内部类方法设置/读取参数映射——因为Reflection无法自动检测内部类方法的参数签名。该单例由 library/Mockery.php 中的\Mockery::getConfiguration()静态方法惰性创建并返回首次调用时实例化new \Mockery\Configuration()并缓存此后所有调用共享同一配置实例。这意味着你在任意测试中修改配置会立即影响整个测试进程内的所有 Mock 行为。默认情况下前两项行为都是启用的。不过文档明确指出这种宽松默认值在某些场景下会带来非预期后果允许 Mock 不存在的方法可能让基于真实类/对象建立的 Mock 与真实实现脱节——尤其当测试没有进行一定程度的集成测试对对象装配关系进行验证时这类偏差很难被发现允许未被满足的期望意味着多余的 Mock 期望不会被察觉从而污染测试代码并可能误导阅读测试的人。开关一是否允许 Mock 不存在的方法\Mockery::getConfiguration()-allowMockingNonExistentMethods(bool);传入true允许该行为传入false禁止该行为。两者都会立即生效直到再次调用切换回来。一旦在禁止状态下检测到Mock 了不存在的方法Mockery 会在该处直接抛出Exception。源码中的默认值与存取逻辑在 Configuration.php 中受保护属性_allowMockingNonExistentMethod默认值为trueallowMockingNonExistentMethods($flag true)将传入值强制转换为布尔并写入读取端由mockingNonExistentMethodsAllowed()返回当前标记。这一开关在哪里真正生效从源码调用链看该标记在三个关键位置被消费Mock 初始化阶段在 library/Mockery/Mock.php 的mockery_init()中若禁止 Mock 不存在方法Mock 对象会把自身通过mockery_getMethods()获取到的真实公共、非静态方法名收集进_mockery_mockableMethods数组作为后续可 Mock 方法白名单。魔术调用阶段同样在 Mock.php 附近调用不存在方法时若禁止标志开启且方法既不存在于 partial 对象、也不可调用parent::$method则该调用会被拦截。Demeter 链构建阶段在 Mockery.php 的buildDemeterChain()中当禁止 Mock 不存在方法、且当前 Mock 不是匿名 Mock、且链首方法不在mockery_getMockableMethods()列表中时会抛出Mockery\Exception错误信息明确提示Mockerys configuration currently forbids mocking the method ... as it does not exist on the class or object being mocked。测试用例佐证仓库测试文件 tests/Mockery/MockTest.php 用多个用例验证了这一开关的行为testShouldIgnoreMissingDisallowMockingNonExistentMethodsUsingGlobalConfiguration先allowMockingNonExistentMethods(false)再对真实类ClassWithMethods的 Mock 调用shouldReceive(nonExistentMethod)预期抛出Mockery\ExceptiontestShouldIgnoreMissingCallingNonExistentMethodsUsingGlobalConfiguration同样的禁止状态下直接调用$mock-nonExistentMethod()预期抛出BadMethodCallExceptiontestShouldIgnoreMissingCallingExistentMethods禁止状态下调用真实存在的方法foo()、bar()仍可正常返回说明该开关只拦截不存在的方法testMockWithNotAllowingMockingOfNonExistentMethodsCanBeGivenAdditionalMethodsToMockEvenIfTheyDontExistOnClass即便全局禁止仍可通过shouldAllowMockingMethod(testSomeNonExistentMethod)显式放行单个不存在的方法。值得留意的是testAnonymousMockWorksWithNotAllowingMockingOfNonExistentMethods表明匿名 Mock不基于任何类不受此限制——这正与buildDemeterChain()中!$mock-mockery_isAnonymous()的判断逻辑相互印证匿名 Mock 没有真实类可参照自然谈不上不存在的方法。开关二是否允许未被满足的期望\Mockery::getConfiguration()-allowMockingMethodsUnnecessarily(bool);同样的语义true允许false禁止立即生效。该行为针对的是设置了期望例如使用了zeroOrMoreTimes()这类零次或多次约束却从未被调用的冗余期望。默认true时这些冗余期望会被静默忽略一旦禁止检测到未使用的期望就会抛出Exception。在源码 Configuration.php 中对应属性_allowMockingMethodsUnnecessarily默认值为true写入端allowMockingMethodsUnnecessarily($flag true)同样做了布尔强制转换读取端为mockingMethodsUnnecessarilyAllowed()。启用这一约束后测试代码中写了却永远用不到的期望会被立即揪出促使开发者清理冗余断言让测试意图更清晰、更贴近被测对象的真实交互。文档特别提醒禁用这两项行为都应谨慎权衡因为它们必然会削减去 Mockery 的一部分灵活性——例如过度依赖真实类方法存在性检查会让以宽松 Mock著称的测试风格变得寸步难行。能力三内部类方法的参数映射Reflection 的盲区补丁\Mockery::getConfiguration()-setInternalClassMethodParamMap($class, $method, array $paramMap); \Mockery::getConfiguration()-getInternalClassMethodParamMap($class, $method);这两个方法用于为PHP 内部类internal classes例如 SPL 类或 PECL 扩展类如 ext/mongo 的MongoCollection的方法定义参数签名。原因是 PHP 的Reflection无法分析内部类方法的参数Mockery 也就无法得知这些方法的参数信息。绝大多数情况下你根本不需要用到它它主要出现在内部类方法的某个参数是按引用传递pass-by-reference的场景——此时必须确保参数签名中正确包含符号因为 Mockery 无法为内部类自动补上这个符号。源码实现细节在 Configuration.php 中setInternalClassMethodParamMap($class, $method, array $map)将映射写入_internalClassParamMap类名与方法名都会被strtolower()归一化后存储因此调用时大小写不敏感getInternalClassMethodParamMap($class, $method)按同样的小写规则读取未命中时返回null配套提供resetInternalClassMethodParamMaps()可清空全部已覆盖的参数映射以及getInternalClassMethodParamMaps()返回整个映射数组。映射如何流入 Mock 生成流程参数映射的消费点在 library/Mockery/Container.php 的mock()方法中创建MockConfigurationBuilder后立即调用$builder-setParameterOverrides(\Mockery::getConfiguration()-getInternalClassMethodParamMaps())将全局配置中的内部类参数映射注入构建器从而影响后续 Mock 类的生成与方法的签名定义。实战示例MongoCollection::insert() 的按引用参数仓库文档 pass_by_reference_behaviours.rst 给出了完整示例。MongoCollection是 PECL mongo 扩展提供的内部类其insert()方法第一个参数是数据数组第二个是可选选项数组原始数据数组会被更新——即insert()含有一个按引用传递的参数调用后数据中会新增_id字段。Mockery 无法用 Reflection 分析这一点因此需要手工配置参数映射\Mockery::getConfiguration()-setInternalClassMethodParamMap( MongoCollection, insert, array($data, $options array()) ); $m \Mockery::mock(MongoCollection); $m-shouldReceive(insert)-with( \Mockery::on(function($data) { if (!is_array($data)) return false; $data[_id] 123; return true; }), \Mockery::any() ); $data array(a1,b2); $m-insert($data); $this-assertTrue(isset($data[_id])); $this-assertEquals(123, $data[_id]); \Mockery::resetContainer();注意三点实操要点参数签名数组中$data的必须显式写出这是 Mockery 无法自动补充的关键信息通过\Mockery::on()闭包参数匹配器模拟按引用修改闭包接收$data在内部写入_id字段使外部变量$data同步被修改测试结束后调用\Mockery::resetContainer()清理容器与配置状态避免影响后续测试。这段代码同时是仓库 Mockery.php 中parseShouldReturnArgs与Expectation机制配合工作的典型范例with()中多个参数依次用\Mockery::on()闭包和\Mockery::any()匹配器进行校验。配置的作用范围、生命周期与 Laravel 实践建议由于配置对象是全局单例且改动立即生效在实际测试中需要特别注意以下几点作用范围所有三项配置均作用于整个测试进程而非单个 Mock 或单个测试方法生命周期配置在进程内持续累积若在某测试中关闭了某行为需在测试结束后显式恢复仓库测试如 MockTest.php 的模式即是allowMockingNonExistentMethods(false)用完后立即...-allowMockingNonExistentMethods(true)还原或直接\Mockery::resetContainer()优先考虑局部约束全局开关通常适合整批测试套件级别的统一约束例如在setUp()或测试基类中统一关闭某行为若只想约束单个 Mock优先使用shouldAllowMockingMethod()、shouldAllowMockingProtectedMethods()等局部 API避免全局状态污染按引用参数是硬性需求凡是涉及内部类方法的按引用参数 MocksetInternalClassMethodParamMap()不是可选项而是必选项否则生成的 Mock 方法签名与真实方法不一致行为将无法正确模拟。在 Laravel 项目中Mockery 通常经 PHPUnit 的测试监听器与Mockery\Adapter\Phpunit集成见仓库 vendor 目录下 MockeryPHPUnitIntegration.php。当你需要严禁 Mock 幽灵方法或杜绝冗余期望这类团队级测试纪律时可将\Mockery::getConfiguration()的开关集中放置在测试基类的初始化逻辑中统一启用再通过上述局部 API 按需放行即可在保留 Mockery 灵活性的同时获得更严格的测试保障。小结Mockery 的全局配置对象通过三个精炼的 API 族将宽松与严谨之间的尺度交还给了测试编写者allowMockingNonExistentMethods()控制 Mock 与真实类之间的同步纪律allowMockingMethodsUnnecessarily()控制测试代码中的冗余期望setInternalClassMethodParamMap()/getInternalClassMethodParamMap()则弥补了 PHP Reflection 对内部类方法的分析盲区。理解它们各自在 Configuration.php、Mock.php、Container.php 与 Mockery.php 中的生效路径以及 MockTest.php 中的行为验证是写出既灵活又严谨的 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点击查看免费下载相关推荐Mockery 默认期望Default Mock Expectations用 byDefault() 消灭重复的 Mock 配置Mockery 默认期望Default Mock Expectations用 byDefault 消灭重复的 Mock 配置 导读 在 PHP 单元测试中测试开发工具Mockery 全局配置Global Configuration完全指南细粒度控制 mock 行为的官方开关Mockery 全局配置Global Configuration完全指南细粒度控制 mock 行为的官方开关 Mockery 通过一个单例singlet测试开发工具Escrcpy 偏好设置完全指南从全局配置到 Scrcpy 参数映射Escrcpy 偏好设置完全指南从全局配置到 Scrcpy 参数映射 Escrcpy 是一款基于 Scrcpy 的跨平台 Android 设备控制工具。本篇指桌面应用移动开发开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表