sql-server-samples 之 Laravel 示例中的 Mockery 入门:用 Simple Example 吃透 PHP Mock 测试

发布时间:2026/9/23 13:07:22
sql-server-samples 之 Laravel 示例中的 Mockery 入门:用 Simple Example 吃透 PHP 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 示例项目所引入的 Mockery 0.9 库官方入门文档 simple_example.rst 为骨架讲解 Mockery 的核心用法如何用m::mock()创建测试替身、用shouldReceive()声明期望、用times()约束调用次数、用andReturn()预设返回值以及m::close()的清理与校验机制。读完本文你将能够独立编写基于 Mockery 的 PHP 单元测试并在 Laravel 项目如本仓库中的 Todo 示例中无缝复用这套测试思路。场景还原一个没有真实依赖也能测试的Temperature类Mockery 入门文档用一个极其精炼的例子说明了为什么需要 mock。假设我们有如下Temperature类它通过一个注入的服务读取某一地点的温度并计算三次读数的平均值class Temperature { public function __construct($service) { $this-_service $service; } public function average() { $total 0; for ($i0;$i3;$i) { $total $this-_service-readTemp(); } return $total/3; } }关键点在于$service目前并不存在——它可能来自 Web 服务或任何其他数据源。但通过Temperature的调用方式我们已经能推断出它必须具备一个readTemp()方法。这正是面向接口或鸭子类型编程的好处被测类只依赖约定的行为而不是具体的实现类。在编写单元测试时我们不想也暂时无法连接真实的数据源此时就可以用一个mock 对象顶替真实服务从而只验证Temperature自身的逻辑是否正确。这就是 Mockery 的用武之地。第一个 Mockery 测试逐行拆解入门文档给出了完整的测试用例use \Mockery as m; class TemperatureTest extends PHPUnit_Framework_TestCase { public function tearDown() { m::close(); } public function testGetsAverageTemperatureFromThreeServiceReadings() { $service m::mock(service); $service-shouldReceive(readTemp)-times(3)-andReturn(10, 12, 14); $temperature new Temperature($service); $this-assertEquals(12, $temperature-average()); } }这段代码虽然短却涵盖了 Mockery 的全部四个核心概念代码片段作用m::mock(service)创建一个名为service的 mock 对象它不是一个真实类而是运行时动态生成的替身shouldReceive(readTemp)声明该 mock 对象期望被调用readTemp方法-times(3)约束readTemp必须且只能被调用3 次-andReturn(10, 12, 14)三次调用分别依次返回 10、12、14三次读数(10 12 14) / 3 12因此assertEquals(12, $temperature-average())通过。mock 让测试完全摆脱了对真实服务实现、网络与数据的依赖只关注Temperature.average()的计算逻辑本身。需要特别说明的是示例中m::mock(service)传入的是一个字符串名称相当于匿名 mockMockery 同样支持m::mock(ConcreteClass)来 mock 真实类或接口此时生成的替身会保留原类的公开接口签名。深入源码shouldReceive、times与andReturn的底层机制入门示例看起来像是魔法实际上每一环都能在本仓库的 Mockery 0.9 源码中找到对应实现。m::mock()与容器机制在 Mockery.php 中mock()是一个静态快捷方式转发到全局唯一的\Mockery\Container实例见getContainer()。所有 mock 对象都登记在这个容器里这也是为什么测试结束时需要统一清理。shouldReceive(readTemp)的期望管理容器会为 mock 上的每个方法维护一个期望集合。核心实现位于 Expectation.php这个类持有方法名_name、期望参数_expectedArgs、调用计数_actualCount等状态。times(3)的调用次数校验Expectation默认使用 Exact.php 作为计数校验器见_countValidatorClass字段即精确匹配 N 次。_actualCount记录真实调用次数测试收尾时若实际次数与期望次数不符就会抛出计数异常。除Exact外CountValidator 目录下还有AtLeast至少 N 次与AtMost至多 N 次两种校验策略。andReturn(10, 12, 14)的多次返回值队列Expectation中维护了_returnQueue返回值队列与_closureQueue闭包队列。传入多个返回值时它们会被依次排入队列、逐次弹出队列耗尽后如果 mock 仍被调用会触发NoMatchingExpectationException之类的异常提示。这正是三次调用分别返回 10、12、14 的实现原理。m::close()为什么每个测试都要清理 mock入门文档在每个测试的tearDown()中调用m::close()这不是可有可无的仪式而是 Mockery 工作流中至关重要的一环。查看 Mockery.php 的实现可以看到public static function close() { if (is_null(self::$_container)) { return; } self::$_container-mockery_teardown(); self::$_container-mockery_close(); self::$_container null; }它做了两件事mockery_teardown()执行期望校验。前面声明的times(3)等计数约束在此刻被核查调用次数不符会立即失败从而把该调用的没调用、不该调用的多调用这类行为偏差暴露出来mockery_close()并置空容器释放当前测试的 mock 集合避免 mock 对象跨测试残留、互相污染。如果省略m::close()调用次数约束将永远得不到验证测试也就退化成只测返回值、不测交互的半吊子测试。这也是 Mockery 文档强调每个测试都必须关闭的原因。PHPUnit 集成能否省掉tearDown()入门文档末尾的注释给出了进阶提示如果启用 PHPUnit 集成就可以不再需要手写tearDown()方法。在 Adapter/Phpunit 目录下可以看到完整的集成设施MockeryTestCase.phpMockery 提供的测试基类继承它即可自动获得 mock 清理能力MockeryPHPUnitIntegration.php供其他测试基类复用的集成 traitTestListener.php作为 PHPUnit 监听器在测试结束时自动执行Mockery::close()。这意味着你既可以延续入门示例的手动tearDown风格也可以让框架自动管理两种方式殊途同归。在本仓库 Laravel 项目中的落地实践本仓库的 Laravel 示例 是一个基于 Laravel 5.1 的完整 Web 项目其中 Mockery 已被正式声明为开发依赖。查看 composer.jsonrequire-dev: { fzaninotto/faker: ~1.4, mockery/mockery: 0.9.*, phpunit/phpunit: ~4.0, phpspec/phpspec: ~2.1 }mockery/mockery: 0.9.*位于require-dev段意味着它只在开发与测试环境生效生产部署执行composer install --no-dev时不会安装——这也印证了官方 installation.rst 中Mockery 作为开发依赖的定位。项目真实的测试代码位于 tests 目录其中TestCase.php 继承Illuminate\Foundation\Testing\TestCase并引导 Laravel 应用bootstrap/app.php是整个测试套件的地基TodoTest.php 展示了更贴近业务的功能测试通过visit(/todo)、click(...)、see(...)、seeInDatabase(...)验证 Todo 页面的完整用户流程。这些测试走的是 Laravel 的集成测试路线而入门文档中的 Mockery 写法则更适合单元测试场景当被测代码依赖外部服务、数据库连接或第三方 API 时用 mock 替身隔离依赖只验证自身逻辑。两者互补构成了完整的测试分层。小结与下一步通过这个入门示例我们已经掌握了 Mockery 的完整工作循环m::mock()创建 mock 替身shouldReceive()-times()-andReturn()声明期望被调用 N 次并依次返回指定值将 mock 注入被测类执行被测逻辑m::close()或 PHPUnit 集成在测试结束时校验所有期望并清理容器。在此基础上Mockery 还提供了参数匹配器any()、type()、on()等见 Mockery.php、部分 mock、spy()等更高级能力。建议在阅读完本文后对照本仓库 vendor/mockery/mockery/docs 目录下的其他文档逐步深入并尝试为 tests 中的 Todo 场景补充基于 mock 的单元测试把先写 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点击查看免费下载相关推荐从一串照片到可旋转的3D场景COLMAP三维重建最快上手路径从一串照片到可旋转的3D场景COLMAP三维重建最快上手路径 假设你刚绕着一栋建筑拍完 20 张照片想把它还原成可以旋转查看的 3D 模型。你不需要自己建模示例工程数据库教程后端SQL Server In-Memory OLTP 高并发 IoT 数据接入实战基于 sql-server-samples 的 Connected Car 示例解析SQL Server In Memory OLTP 高并发 IoT 数据接入实战基于 sql server samples 的 Connected Car 示示例工程数据库教程后端抖音批量下载教程3 条命令存下博主整个主页抖音批量下载教程3 条命令存下博主整个主页 Douyin Downloader 是一个开源的抖音批量下载工具也叫抖音下载器把博主主页链接粘进配置文件终网页爬虫CLI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考