SQL Server Samples 仓库中的 Laravel 示例:Mockery 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 是一个简洁而灵活的 PHP 模拟对象(mock object)框架,专为 PHPUnit、PHPSpec 等单元测试框架设计。本仓库的 Laravel 示例(samples/development-frameworks/laravel)在composer.json的require-dev中引入了mockery/mockery0.9.*,并将其完整文档随 vendor 目录一并提供(docs/index.rst)。阅读本文后,你将掌握 Mockery 的核心概念、安装方式、Mock 对象创建语法、期望声明(expectation)与参数匹配、PHPUnit 集成等完整技能,可直接在 Laravel 项目的单元测试中落地使用。
Mockery 是什么:面向单元测试的 PHP 模拟对象框架
Mockery 的核心目标是以一套精炼的 API 提供测试替身(test double)能力,用接近人类自然语言的领域特定语言(DSL)清晰定义对象的操作与交互。它被设计为 PHPUnit 自带phpunit-mock-objects的直接替代品,既易于与 PHPUnit 集成,也可以与phpunit-mock-objects共存使用,互不冲突。
在单元测试中,模拟对象用于模拟真实对象的行为,常见用途包括:
- 提供测试隔离,让被测代码不依赖真实外部依赖;
- 替身那些尚不存在的对象(例如依赖尚未开发的接口或服务);
- 在不实现具体代码的前提下,探索式地设计类 API。
模拟对象框架的价值在于:能够灵活生成 Mock 对象与桩(stub),并以可读性极强的 API 设置预期方法调用与返回值,尽可能以接近自然语言的方式捕获真实对象的每一种行为。
在 Laravel 示例中的实际定位
在本仓库的 Laravel 示例(composer.json)中,Mockery 被声明为开发期依赖:
"require-dev": { "fzaninotto/faker": "~1.4", "mockery/mockery": "0.9.*", "phpunit/phpunit": "~4.0", "phpspec/phpspec": "~2.1" }可见该项目同时使用 PHPUnit 4.x 与 PHPSpec 2.1 作为测试框架,Mockery 作为两者共用的模拟框架被固定为 0.9.x 版本。由于它位于require-dev段,生产环境执行composer install --no-dev时不会安装。这也与本文档所在位置吻合——Mockery 完整文档被打包在 vendor/mockery/mockery/docs 目录下,分为 Getting Started、Reference、Cookbook 三大部分。
安装与引入 Mockery
通过 Composer 安装
首先为项目安装 Composer,然后在composer.json的require-dev中声明依赖。文档建议使用稳定的版本标签(如@stable),也可以直接引用开发分支:
{ "require-dev": { "mockery/mockery": "dev-master" } }随后执行更新命令即可完成安装:
php composer.phar update安装后的 Mockery 仅作为开发依赖存在,生产环境执行php composer.phar update --no-dev时不会被安装。本仓库 Laravel 示例的 composer.json 即采用该模式,并锁定为0.9.*版本。
通过 PEAR 安装
Mockery 托管在独立的 PEAR 频道上,可使用如下命令安装(会同时安装 Hamcrest 依赖):
sudo pear channel-discover pear.survivethedeepend.com sudo pear channel-discover hamcrest.googlecode.com/svn/pear sudo pear install --alldeps deepend/Mockery通过 Git 仓库安装
Mockery 的 master 分支承载开发版本。可以使用 Composer 引用dev-master版本,也可以直接克隆仓库后通过 PEAR 安装。安装过程会同时安装 Mockery 与 Hamcrest——Hamcrest 并非 Mockery 运行的硬性依赖,但强烈建议安装,因为它为参数匹配(argument matching)提供更丰富的匹配器(matcher)能力。
引入自动加载
Mockery 自带自动加载器(autoloader),无需在测试中手动写require_once()。使用时只需确保 Mockery 位于include_path中,并在测试套件的Bootstrap.php或TestHelper.php中注册:
require_once 'Mockery/Loader.php'; require_once 'Hamcrest/Hamcrest.php'; $loader = new \Mockery\Loader; $loader->register();如果使用 Composer,则只需引入 Composer 生成的自动加载文件即可:
require __DIR__ . '/../vendor/autoload.php'; // 假设 vendor 位于上一级目录注意:Hamcrest 1.0.0 之前,
Hamcrest.php文件名是小写hamcrest.php。升级到 1.0.0 后,请检查所有项目中该文件名是否已更新。
快速上手:第一个 Mockery 测试
假设有一个Temperature类,它通过外部服务采样某地区的温度并计算平均值。数据可能来自 Web 服务或其他数据源,但当前我们并没有这样的服务类。不过,基于Temperature类对服务的调用方式,可以推断出服务类应具备的基本交互:
class Temperature { public function __construct($service) { $this->_service = $service; } public function average() { $total = 0; for ($i=0;$i<3;$i++) { $total += $this->_service->readTemp(); } return $total/3; } }即便没有真实的服务类,也能清楚看到Temperature对它的预期:readTemp()方法将被调用 3 次。编写测试时,可以用 Mock 对象替代真实服务,从而在无需具体服务实例的情况下验证Temperature的行为:
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()); } }这段测试的核心逻辑是:通过shouldReceive('readTemp')->times(3)->andReturn(10, 12, 14)声明期望——readTemp必须恰好被调用 3 次,依次返回 10、12、14。Temperature::average()对 3 次读数求和后取平均得到 12,与断言一致。tearDown()中的m::close()负责清理当前测试的 Mockery 容器并执行期望验证;若使用 PHPUnit 集成(见后文),则可以省略该tearDown()。
创建 Mock 对象的完整语法
\Mockery::mock()的第一个参数决定 Mock 的类型与行为,第二个参数可以是期望数组或期望闭包(配合期望数组时可为第三参数),下面是文档中列举的全部创建方式。
命名 Mock(最宽松的形式)
$mock = \Mockery::mock('foo');创建一个名为 "foo" 的 Mock 对象。这里的 "foo" 是名称(不一定是类名),仅作为抛出异常时的简单标识。它生成\Mockery\Mock类型的对象,是所有 Mock 中约束最宽松的形式。
通过期望数组快速设置
$mock = \Mockery::mock(array('foo'=>1,'bar'=>2));未指定名称,但传入了期望数组——这是快速声明"期望哪些方法、返回什么值"的简便方式。期望数组可以作为第二个参数传给所有形式的mock():
$mock = \Mockery::mock('foo', array('foo'=>1,'bar'=>2));通过闭包设置可复用期望
$mock = \Mockery::mock('foo', function($mock) { $mock->shouldReceive(method_name); });除了期望数组,还可以传入包含可复用期望的闭包,作为第二个参数;若与期望数组并用,则作为第三个参数。这是实现可复用 Mock 期望的方式之一。
基于具体类创建 Mock
$mock = \Mockery::mock('stdClass');当名称是真实类名时,Mock 对象将通过继承获得该类类型,可通过类型提示(type hint)或instanceof校验,适合要求 Mock 必须是特定类型的场景。
基于接口、抽象类创建 Mock
$mock = \Mockery::mock('FooInterface');任何具体类、抽象类甚至接口都可以作为 Mock 基础,主要目的同样是让 Mock 继承特定类型以通过类型提示。例外是:被标记为final的类或含final方法的类无法被完整 Mock,此时必须使用部分 Mock(partial mock)。
别名 Mock(Alias Mock):模拟静态方法
$mock = \Mockery::mock('alias:MyNamespace\MyClass');给一个"当前尚未加载"的类名加上alias:前缀,会生成"别名 Mock":为该类名创建指向stdClass的类别名,通常用于模拟公共静态方法。设置在 Mock 对象上、针对静态方法的期望,将作用于该类的所有静态调用。
实例 Mock(Instance Mock / overload)
$mock = \Mockery::mock('overload:MyNamespace\MyClass');overload:前缀与alias:类似,但区别在于:之后new出来的该类实例会导入在源 Mock($mock)上设置的期望。源 Mock 本身永远不会被验证,它仅作为新实例的期望存储,故称为"实例 Mock"(instance mock),以区别于更简单的"别名 Mock"。
警告:在同一测试中多次使用 alias/instance Mock 会因无法定义同名类而产生致命错误。解决方法是让这类测试各自运行在独立的 PHP 进程中(PHPUnit 与 PHPT 均原生支持)。
同时实现多个接口
$mock = \Mockery::mock('stdClass, MyInterface1, MyInterface2');第一个参数也可以是用逗号分隔的接口列表,其中最多可包含一个现有类作为基础。类名不要求位于列表首位,但按惯例放在开头更易读。若给定的类不存在,则必须事先定义并包含它,否则会抛出\Mockery\Exception。
传统部分 Mock(Partial Mock)
$mock = \Mockery::mock('MyNamespace\MyClass[foo,bar]');方括号语法告诉 Mockery 对MyNamespace\MyClass进行部分 Mock:仅模拟foo()与bar()两个方法,其余方法不被 Mockery 覆盖。这种传统形式适用于任何类或抽象类(例如模拟尚无具体实现的抽象方法)。如果尝试部分 Mock 一个final方法,该实例中会被忽略,final 方法保持原样——因为按 PHP 定义,final 方法本就无法被 Mock。
传递构造函数参数
$mock = \Mockery::mock("MyNamespace\MyClass[foo]", array($arg1, $arg2));当 Mockery 遇到索引数组作为第二或第三参数时,会将其视为构造函数参数,在构造 Mock 对象时传入。上面的语法创建了一个新的部分 Mock,特别适合bar方法内部通过$this->foo()调用foo的场景。
代理部分 Mock(Proxied Partial Mock)
$mock = \Mockery::mock(new Foo);向 Mockery 传入任意真实对象会创建"代理部分 Mock"。当真实部分 Mock 不可能实现时(例如 final 类,或必须覆盖 final 方法),此方式非常有用:既然已经能创建具体对象,只需选择性覆盖现有方法的子集(甚至可以添加不存在的非现有方法)来满足期望。
复习:参数约定
所有 Mock 方法都以"要 Mock 的类、对象或别名"作为第一个参数;第二个参数可以是"方法到返回值的期望数组"或"期望闭包"(与期望数组并用时闭包为第三参数)。
\Mockery::self():链式返回自身
\Mockery::self()当期望中的方法需要返回同一个 Mock 对象时(例如设计 DSL 时——Mockery 自身就是这样的例子),调用\Mockery::self()会返回最近一次通过\Mockery::mock()创建的 Mock 对象:
$mock = \Mockery::mock('BazIterator') ->shouldReceive('next') ->andReturn(\Mockery::self()) ->mock();以迭代器为例,很多场景下可以把所有迭代元素(往往类型相同)替换为同一个 Mock 对象,让它轮流扮演各个离散元素。
namedMock:按自定义名称生成类
$mock = \Mockery::namedMock('MyClassName', 'DateTime');namedMock会生成一个以第一个参数为类名的类(如MyClassName),其余参数处理方式与mock相同。例如上述调用创建名为MyClassName、继承DateTime的类。命名 Mock 属于边缘场景,但当代码依赖__CLASS__魔术常量,或需要某个抽象类型的两个不同派生类时,它很有用。
警告:命名 Mock 只能创建一次,后续以不同参数再次调用
namedMock很可能引发异常。
行为修饰符:改变 Mock 的默认行为
shouldIgnoreMissing():被动 Mock
\Mockery::mock('MyClass')->shouldIgnoreMissing()该修饰符将 Mock 标记为"被动 Mock"(Passive Mock):调用未被期望覆盖的方法时,返回null,而不是通常那样抱怨"没有匹配该调用的期望"。若希望恢复 0.7.2 时代的行为——返回\Mockery\Undefined类型的对象(即空对象),可叠加:
\Mockery::mock('MyClass')->shouldIgnoreMissing()->asUndefined()返回的空对象只是一个占位符,如果被误用在不该出现的地方,通常无法通过逻辑检查。
makePartial() / shouldDeferMissing():被动部分 Mock
\Mockery::mock('MyClass')->makePartial()\Mockery::mock('MyClass')->shouldDeferMissing()这两种写法等价,称为"被动部分 Mock"(Passive Partial Mock,区别于后文讨论的真实部分 Mock 对象)。它对未受期望约束的所有方法,会委托给 Mock 的父类(即MyClass)执行。与前面shouldIgnoreMissing()返回null不同,此行为会直接调用父类的对应方法。
期望声明(Expectation Declarations):定义 Mock 的行为与调用方式
创建 Mock 对象后,下一步就是通过期望声明定义它的行为与调用约束。
声明期望的方法
shouldReceive(method_name)声明 Mock 期望收到对指定方法的调用,这是所有后续期望与约束的起点。
shouldReceive(method1, method2, ...)一次声明多个期望方法,所有方法共享后续链式期望或约束。
shouldReceive(array('method1'=>1, 'method2'=>2, ...))声明多个期望调用并同时指定返回值的数组形式。
shouldReceive(closure)仅从部分 Mock 创建:生成"Mock 对象记录器"(recorder),它是原始对象的简单代理,闭包可对其执行一系列操作,这些操作被记录为部分 Mock 上的期望。典型场景是在重构时基于已有用法自动录制期望。
shouldNotReceive(method_name)声明 Mock 不应收到对指定方法的调用,等价于shouldReceive()->never()。
参数约束
with(arg1, arg2, ...) / withArgs(array(arg1, arg2, ...))为该期望增加"参数列表必须匹配"的约束。可以借助内置匹配器大幅增强灵活性,例如\Mockery::any()匹配该位置的任意参数;Mockery 也允许使用 Hamcrest 匹配器(如 Hamcrest 的anything()等价于\Mockery::any())。这意味着同一方法可依据不同参数设置不同期望——期望只在参数完全匹配时才生效。
withAnyArgs()声明该期望匹配任何参数的方法调用(未特别指定时是默认行为)。
withNoArgs()声明该期望只匹配零参数的方法调用。
返回值设置
andReturn(value)设置期望方法调用的返回值。
andReturn(value1, value2, ...)设置返回值序列或闭包序列:第一次调用返回 value1,第二次返回 value2。注意:之后对该 Mock 方法的所有调用,都始终返回最后(或唯一)给定的值。
andReturnNull() / andReturn([NULL])两种写法主要是向测试读者传达"该方法返回 null 或什么都不返回"。
andReturnValues(array)andReturn()的替代语法,接受数组而非参数列表。返回顺序由数组数字索引决定,先前返回值耗尽后,后续调用返回最后一个数组成员。
andReturnUsing(closure, ...)设置一个闭包,用传入方法的参数调用它,并将闭包返回值作为结果返回。适合对参数做动态加工后得到相关结果;闭包可通过额外参数排队,用法同andReturn()。
注意:当前不能混用
andReturnUsing()与andReturn()。
andThrow(Exception)声明该方法被调用时抛出给定的Exception对象。
andThrow(exception_name, message)也可以不传对象,而传异常类名与消息,由 Mock 方法抛出新异常。
andSet(name, value1) / set(name, value1)配合期望使用:当匹配的方法被调用时,同时将 Mock 对象的公共属性设置为指定值。
passthru()让期望绕过返回值队列,转而调用被 Mock 类的真实方法并返回结果。本质上,它允许对真实方法施加期望匹配与调用次数验证,同时仍然以期望的参数调用真实类方法。
调用次数约束
zeroOrMoreTimes()声明期望方法可被调用零次或多次(所有方法未另行指定时的默认行为)。
once()声明方法只能被调用一次。与所有调用次数约束一样,违反时会抛出\Mockery\CountValidator\Exception,并可被atLeast()与atMost()修改。
twice()声明方法只能被调用两次。
times(n)声明方法只能被调用 n 次。
never()声明方法绝不能被调用——永远!
atLeast()为下一个调用次数期望添加最小值修饰,atLeast()->times(3)表示调用必须至少 3 次。
atMost()添加最大值修饰,atMost()->times(3)表示调用不得超过 3 次,也允许零次调用。
between(min, max)设置调用次数的区间,等价于atLeast()->times(min)->atMost()->times(max)的简写;可跟一个无参的times()以保持 API 的自然语言可读性。
调用顺序约束
ordered()声明该方法必须在与同样标记的方法之间按特定顺序被调用,顺序由设置期望时使用该修饰符的顺序决定。
ordered(group)将方法声明为某个顺序组(可命名或编号)的成员。组内方法可任意顺序调用,但组外有序调用相对该组是有序的——例如可设置 method1 在 group1 之前、group1 在 method2 之前。
globally()在ordered()或ordered(group)之前调用,声明该顺序约束作用于所有 Mock 对象(而非仅当前 Mock),从而跨多个 Mock 规定调用顺序。
默认期望与链式取回
byDefault()将期望标记为默认期望。默认期望在未创建非默认期望时生效,后创建的非默认期望会立即替换先前定义的默认值。这样可以在setup()中设置默认 Mock,再在具体测试中按需微调。
getMock()从期望链中返回当前 Mock 对象,便于将 Mock 设置保持为单条语句:
$mock = \Mockery::mock('foo')->shouldReceive('foo')->andReturn(1)->getMock();参数匹配与验证(Argument Validation)
with()中传入的参数决定了"调用如何匹配期望"。可以为同一方法设置多个期望,各自按参数区分。匹配采用"最佳匹配"(best fit)原则:显式匹配优先于泛化匹配。
显式匹配指期望参数与实际参数容易判定相等(===或==);更泛化的匹配可通过正则表达式、类提示(class hinting)与内置匹配器实现。Mockery 内置匹配器并未覆盖所有可能,但提供对 Hamcrest 匹配器库的可选支持(Hamcrest 是 Java 同名库的 PHP 移植版,同时也有 Python、Erlang 等版本)。由于 Hamcrest 已提供出色的自然语言 DSL 工具集,Mockery 没有必要重复造轮子。
内置匹配器一览
with(1)匹配整数1,通过===(全等)判定;也允许较宽松的==(等于)判定,此时字符串'1'也能匹配该参数。
with(\Mockery::any()) OR with(anything())匹配任意参数(anything()为 Hamcrest 等价写法)。
with(\Mockery::type('resource')) OR with(resourceValue()) OR with(typeOf('resource'))匹配任意资源类型,即is_resource()返回 true。Type 匹配器接受任意可拼成is_前缀的有效类型检查字符串,例如\Mockery::type('float')对应is_float()检查,\Mockery::type('callable')对应is_callable()检查。Type 匹配器也接受类名或接口名,用于对实际参数做instanceof判定(Hamcrest 对应anInstanceOf())。
with(\Mockery::on(closure))On 匹配器接受一个闭包,实际参数会被传入其中;闭包返回布尔true即视为匹配。当参数期望过于复杂、超出默认匹配器能力时,此方法价值极大。Hamcrest 无对应实现。
with('/^foo/') OR with(matchesPattern('/^foo/'))参数声明器也假定任意字符串可能是正则表达式。正则匹配仅在两种情况同时满足时启用:a) 不存在===或==匹配;b) 该字符串被验证为合法正则(即preg_match()不返回 false)。若不喜欢隐式正则检测,Hamcrest 提供更显式的matchesPattern()函数。
with(\Mockery::ducktype('foo', 'bar'))Ducktype 匹配器是类类型匹配的替代方案:匹配任何"包含给定方法列表"的对象(即该对象可调用foo、bar方法)。Hamcrest 无对应实现。
with(\Mockery::mustBe(2)) OR with(identicalTo(2))MustBe 匹配器比默认匹配器更严格:默认匹配器允许 PHP 类型转换(字符串'2'可匹配整数 2),而 MustBe 要求参数与期望值类型相同——同一场景下'2'(字符串)匹配整数 2 会被 MustBe 判定失败。注意:该匹配器对对象不做全等比较,因为 PHP 要求两个对象为同一实例才全等;当对象在返回前才生成时,全等匹配几乎不可能成立。
with(\Mockery::not(2)) OR with(not(2))Not 匹配器匹配任何"不等于或不全等于"其参数的值。
with(\Mockery::anyOf(1, 2)) OR with(anyOf(1,2))匹配等于给定参数中任意一个的值。
with(\Mockery::notAnyOf(1, 2))匹配不等于也不全等于给定参数中任何一个的值。Hamcrest 无对应实现。
with(\Mockery::subset(array(0 => 'foo')))匹配任何"包含给定数组子集"的数组,同时比较键名与键值。Hamcrest 无对应实现,但它可以用hasEntry()或hasKeyValuePair()检查单条条目。
with(\Mockery::contains(value1, value2))匹配任何"包含所列值"的数组,忽略键名。
with(\Mockery::hasKey(key));匹配任何"包含给定键名"的数组。
with(\Mockery::hasValue(value));匹配任何"包含给定值"的数组。
与 PHPUnit 集成
Mockery 是设计为简单易用的独立模拟框架,与测试框架的集成完全可选。最基本的集成方式是在测试中定义tearDown()并调用:
public function tearDown() { \Mockery::close(); }该静态调用会清理当前测试使用的 Mockery 容器,并执行期望所需的验证任务。为书写简便,可用更短的别名:
use \Mockery as m; class SimpleTest extends PHPUnit_Framework_TestCase { public function testSimpleMock() { $mock = m::mock('simplemock'); $mock->shouldReceive('foo')->with(5, m::any())->once()->andReturn(10); $this->assertEquals(10, $mock->foo(5)); } public function tearDown() { m::close(); } }自动清理:TestListener
若要避免手动调用 close 方法,并让 Mockery 从代码覆盖率报告中移除自身,可在测试套件中注册 Mockery 的 PHPUnit 监听器:
// 创建套件 $suite = new PHPUnit_Framework_TestSuite(); // 创建结果监听器并添加 $result = new PHPUnit_Framework_TestResult(); $result->addListener(new \Mockery\Adapter\Phpunit\TestListener()); // 运行测试 $suite->run($result);若使用 PHPUnit 的 XML 配置方式,可在配置中加入:
<listeners> <listener class="\Mockery\Adapter\Phpunit\TestListener"></listener> </listeners>务必确保 Composer 或 Mockery 的自动加载器已出现在 bootstrap 文件中,否则还需为该TestListener类定义file属性指向其所在文件。
进程隔离的注意事项
PHPUnit 支持让测试在独立进程中运行以获得更好的隔离性。Mockery 通过Mockery::close()验证期望,其 PHPUnit 监听器会在每个测试后自动调用该方法。但使用进程隔离时,该监听器不会在正确的进程中执行,导致期望可能未被验证,且不会抛出任何Mockery\Exception。因此在使用进程隔离时,不能依赖 Mockery 自带的 PHPUnitTestListener,必须在tearDown()中显式调用Mockery::close()(即本文前述最基础的方式)。
升级注意事项
文档记录了 0.8 与 0.9 两个版本的关键行为变更,如果你在 Laravel 示例中升级mockery/mockery版本(当前锁定 0.9.*),需要注意:
- 升级到 0.9:Mockery 的生成器(generator)被完全重写,任何与 Mockery 深度集成的代码都需要重新评估。
- 升级到 0.8:
shouldIgnoreMissing()行为变化——0.7.2 及更早版本中,未匹配已知期望的方法调用返回\Mockery\Undefined实例;自 0.8.0 起改为返回null。可显式恢复旧行为:
$mock = \Mockery::mock('stdClass')->shouldIgnoreMissing()->asUndefined();深入阅读:更多参考文档与 Cookbook
本仓库随包提供了完整的 Mockery 文档集(docs),除本文覆盖的内容外,还可按需查阅:
- 快速参考:reference/startup_methods.rst(Mock 创建与行为修饰符全解)
- 期望声明:reference/expectations.rst
- 参数匹配:reference/argument_validation.rst
- PHPUnit 集成:reference/phpunit_integration.rst
- 部分 Mock:reference/partial_mocks.rst
- 公共属性 / 静态属性:reference/public_properties.rst、reference/public_static_properties.rst
- 引用传递行为:reference/pass_by_reference_behaviours.rst
- Demeter 链:reference/demeter_chains.rst
- 对象录制:reference/object_recording.rst
- final 类与 final 方法:reference/final_methods_classes.rst
- 魔术方法:reference/magic_methods.rst
- 配置 / 异常 / 保留方法名 / 易错点:reference/mockery/configuration.rst、reference/mockery/exceptions.rst、reference/mockery/reserved_method_names.rst、reference/mockery/gotchas.rst
- Cookbook 技巧:cookbook/index.rst(含默认期望、Mock 对象检测、硬依赖 Mock 等实战技巧,见 cookbook 目录)
在 Laravel 项目的单元测试实践中,建议以 PHPUnit 的tearDown()显式调用Mockery::close()作为稳健基线,再按需引入 TestListener 简化写法;使用shouldReceive系列声明期望、配合参数匹配器精确控制调用约束,即可获得与自然语言高度一致、可读且可维护的测试代码。
【免费下载链接】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),仅供参考