用嘲笑嘲笑您的测试依赖性
虽然并非每个人都在这样做,但测试你的应用程序是作为开发人员最基本的部分之一。单元测试是最常见的测试。通过单元测试,你可以检查一个类是否完全按照你的预期那样运行。有时,你在你的应用程序中使用的是第三方服务,很难设置好所有内容来进行单元测试。这正是模拟发挥作用的时候。
关键要点
- 模拟是创建一个替代单元测试中真实对象的替代对象的过程,这在测试严重依赖依赖注入的应用程序时特别有用。
- Mockery 是 Pádraic Brady 创建的一个库,可用于模拟单元测试中的对象,为 PHPUnit 的默认模拟功能提供了一种替代方案。
- Mockery 允许开发人员定义对方法调用次数、将接收的参数以及将返回的值的期望,使其成为隔离单元测试中依赖项的强大工具。
- 虽然 PHPUnit 已经可以模拟对象,但 Mockery 为希望确保其单元测试不受其他类影响的开发人员提供了更大的灵活性和便利性。
什么是模拟?
模拟对象只不过是创建一个替代对象,它在单元测试中替换真实对象。如果你的应用程序严重依赖依赖注入,模拟是可行的方法。
模拟对象可能有几个原因:
- 执行单元测试时,最好隔离类。你不想让另一个类或服务干扰你的单元测试。
- 对象尚不存在。你可以先创建测试,然后构建最终对象。
- 模拟对象通常比为测试准备整个数据库更快。
运行单元测试时,你可能正在使用 PHPUnit。PHPUnit 带有一些默认的模拟功能,如文档中所示。你可以在 Jeune Asuncion 撰写的这篇文章中阅读更多关于模拟的常规信息以及 PHPUnit 的模拟功能。
在本文中,我们将深入探讨由 Pádraic Brady 创建的库 Mockery。我们将创建一个温度类,该类将注入当前不存在的天气服务。
设置
让我们从设置项目开始。我们从包含以下内容的 composer.json 文件开始。这将确保我们拥有 mockery 和 PHPUnit。
<code>{ "name": "sitepoint/weather", "license": "MIT", "type": "project", "require": { "php": ">=5.3.3" }, "autoload": { "psr-0": { "": "src/" } }, "require-dev": { "phpunit/phpunit": "4.1.*", "mockery/mockery": "0.9.*" } }</code>
我们还创建一个名为 phpunit.xml 的 PHPUnit 配置文件
<phpunit> <testsuite name="SitePoint Weather"> <directory>./tests</directory> </testsuite> <listeners> <listener class="\Mockery\Adapter\Phpunit\TestListener" file="vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php"/> </listeners> </phpunit>
定义这个监听器很重要。如果没有监听器,如果方法 once()
、twice()
和 times()
使用不正确,则不会引发错误。稍后将详细介绍。
我还创建了 2 个目录。src 目录用于保存我的类,tests 目录用于存储我们的测试。在 src 目录中,我创建了路径 SitePointWeather。
我们首先创建 WeatherServiceInterface。我们不存在的天气服务将实现此接口。在这种情况下,我们只提供一个方法,该方法将为我们提供摄氏温度。
<code>{ "name": "sitepoint/weather", "license": "MIT", "type": "project", "require": { "php": ">=5.3.3" }, "autoload": { "psr-0": { "": "src/" } }, "require-dev": { "phpunit/phpunit": "4.1.*", "mockery/mockery": "0.9.*" } }</code>
因此,我们有一个服务可以为我们提供摄氏温度。我想获得华氏温度。为此,我创建了一个名为 TemperatureService 的新类。此服务将注入天气服务。除此之外,我们还定义了一个方法,该方法将摄氏温度转换为华氏温度。
<phpunit> <testsuite name="SitePoint Weather"> <directory>./tests</directory> </testsuite> <listeners> <listener class="\Mockery\Adapter\Phpunit\TestListener" file="vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php"/> </listeners> </phpunit>
创建单元测试
我们已经准备好设置单元测试了。我们在 tests 目录中创建一个 TemperatureServiceTest 类。在这个类中,我们创建方法 testGetTempFahrenheit()
,它将测试我们的华氏方法。
此方法中要做的第一步是创建一个新的 TemperatureService 对象。就在我们这样做的时候,我们的构造函数将请求一个实现了 WeatherServiceInterface 的对象。由于我们还没有这样的对象(我们也不想要),我们将使用 Mockery 为我们创建一个模拟对象。让我们看看完成后的方法是什么样的。
namespace SitePoint\Weather; interface WeatherServiceInterface { /** * 返回摄氏温度 * * @return float */ public function getTempCelsius(); }
我们首先创建模拟对象。我们告诉 Mockery 我们想要模拟哪个对象(或接口)。第二步是描述将在此模拟对象上调用的方法。在 shouldReceive()
方法中,我们定义将调用的方法的名称。
我们定义此方法将调用的次数。我们可以使用 once()
、twice()
和 times(X)
。在这种情况下,我们预计它只会调用一次。如果未调用或调用次数过多,单元测试将失败。
最后,我们在 andReturn()
方法中定义将返回的值。在这种情况下,我们返回 25。Mockery 还具有 andReturnNull()
、andReturnSelf()
和 andReturnUndefined()
等返回方法。如果这是你的预期,Mockery 也能够抛出异常。
我们现在有了模拟对象,可以创建我们的 TemperatureService 对象并像往常一样进行测试。25 摄氏度是 77 华氏度,因此我们检查是否从我们的 getTempFahrenheit()
方法中收到 77。
如果你在你的根目录中运行 vendor/bin/phpunit tests/
,你将从 PHPUnit 获得绿灯,表明一切都很完美。
高级用法
上面的例子相当简单。没有参数,只是一个简单的调用。让我们让事情变得复杂一些。
假设我们的天气服务还有一个方法可以在确切的小时获取温度。我们将以下方法添加到我们当前的 WeatherServiceInterface。
namespace SitePoint\Weather; class TemperatureService { /** * @var WeatherServiceInterace $weatherService 保存天气服务 */ private $weatherService; /** * 构造函数。 * * @param WeatherServiceInterface $weatherService */ public function __construct(WeatherServiceInterface $weatherService) { $this->weatherService = $weatherService; } /** * 获取当前华氏温度 * * @return float */ public function getTempFahrenheit() { return ($this->weatherService->getTempCelsius() * 1.8000) + 32; } }
我们想知道,晚上 0:00 到 6:00 之间的平均温度是多少。为此,我们在 TemperatureService 中创建一个新方法来计算平均温度。为此,我们从 WeatherService 中检索 7 个温度并计算平均值。
<code>{ "name": "sitepoint/weather", "license": "MIT", "type": "project", "require": { "php": ">=5.3.3" }, "autoload": { "psr-0": { "": "src/" } }, "require-dev": { "phpunit/phpunit": "4.1.*", "mockery/mockery": "0.9.*" } }</code>
让我们看看我们的测试方法。
<phpunit> <testsuite name="SitePoint Weather"> <directory>./tests</directory> </testsuite> <listeners> <listener class="\Mockery\Adapter\Phpunit\TestListener" file="vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php"/> </listeners> </phpunit>
我们再次模拟接口,并定义将调用的方法。接下来,我们定义此方法将调用的次数。我们在前面的示例中使用了 once()
,现在我们使用 times(7)
来指示我们期望此方法被调用 7 次。如果该方法没有被精确调用 7 次,则测试将失败。如果你没有在 phpunit.xml 配置文件中定义监听器,你将不会收到关于此的通知。
接下来,我们定义 with()
方法。在 with
方法中,你可以定义你期望的参数。在这种情况下,我们期望 7 个不同的小时。
最后,我们有 andReturn()
方法。在这种情况下,我们指示了 7 个返回值。如果你定义的返回值较少,则每次都会重复最后一个可用的返回值。
当然,Mockery 可以做更多的事情。有关完整的指南和文档,我建议你查看 Github 页面。
如果你对上面项目的代码感兴趣,你可以查看这个 Github 页面。
结论
使用 PHPUnit,你已经可以模拟对象了。但是,你也可以像上面示例中解释的那样使用 Mockery。如果你正在对你的类进行单元测试,并且你不想让任何其他类影响你的测试,mockery 可以轻松地帮助你。如果你真的想进行功能测试,最好看看你是否可以集成真正的测试。你目前是否正在使用 PHPUnit 模拟并考虑切换到 Mockery?你想在后续文章中看到更多更大的 Mockery 示例吗?请在下面的评论中告诉我。
关于 Mockery 和测试依赖项的常见问题解答 (FAQ)
什么是 Mockery,为什么它在 PHP 测试中很重要?
Mockery 是一个强大而灵活的 PHP 模拟对象框架,用于单元测试。它被设计为 PHPUnit 模拟对象功能的直接替代品。Mockery 允许开发人员隔离被测代码并创建测试替身,这些测试替身模拟复杂对象的行为。这在单元测试中至关重要,因为它确保被测代码不依赖于任何外部因素或状态。
如何在我的 PHP 项目中安装和设置 Mockery?
要安装 Mockery,你需要拥有 Composer,这是一个 PHP 的依赖项管理器。你可以通过运行命令 composer require --dev mockery/mockery
来安装 Mockery。安装后,你可以在测试文件中通过在测试拆卸方法中调用 Mockery::close()
来设置 Mockery,以清理模拟对象。
如何使用 Mockery 创建模拟对象?
在 Mockery 中创建模拟对象很简单。你可以使用 mock()
方法来创建一个模拟对象。例如,$mock = Mockery::mock('MyClass');
将创建一个 MyClass 的模拟对象。
如何在 Mockery 中定义期望?
在 Mockery 中,你通过将方法链接到模拟对象来定义期望。例如,$mock->shouldReceive('myMethod')->once()->andReturn('mocked value');
此代码告诉 Mockery 预期“myMethod”将被调用一次,并且应该返回“mocked value”。
Mockery 中模拟和存根的区别是什么?
在 Mockery 中,模拟是我们可以在其上设置期望的对象,而存根是预先编程了响应的模拟对象。当响应是唯一重要的事情时,通常使用存根,而当测试交互本身时,则使用模拟。
如何使用 Mockery 测试私有方法?
不建议直接测试私有方法,因为它违反了封装原则。但是,如果需要,你可以使用 Mockery 中的 shouldAllowMockingProtectedMethods()
方法来允许模拟受保护和私有方法。
如何在 Mockery 中处理构造函数参数?
如果要模拟的类具有带参数的构造函数,可以将它们作为数组传递给 mock()
方法。例如,$mock = Mockery::mock('MyClass', [$arg1, $arg2]);
将将 $arg1 和 $arg2 传递给 MyClass 的构造函数。
如何使用 Mockery 模拟静态方法?
Mockery 提供了一种使用 alias:
前缀模拟静态方法的方法。例如,$mock = Mockery::mock('alias:MyClass');
将创建一个可以用来对 MyClass 的静态方法设置期望的模拟对象。
如何验证 Mockery 中是否已满足所有期望?
你可以在测试拆卸方法中使用 Mockery::close()
方法来验证是否已满足所有期望。如果任何期望未满足,Mockery 将引发异常。
如何在 Mockery 中处理异常?
你可以使用 andThrow()
方法设置模拟对象以抛出异常。例如,$mock->shouldReceive('myMethod')->andThrow(new Exception);
将在调用“myMethod”时抛出异常。
以上是用嘲笑嘲笑您的测试依赖性的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JWT是一种基于JSON的开放标准,用于在各方之间安全地传输信息,主要用于身份验证和信息交换。1.JWT由Header、Payload和Signature三部分组成。2.JWT的工作原理包括生成JWT、验证JWT和解析Payload三个步骤。3.在PHP中使用JWT进行身份验证时,可以生成和验证JWT,并在高级用法中包含用户角色和权限信息。4.常见错误包括签名验证失败、令牌过期和Payload过大,调试技巧包括使用调试工具和日志记录。5.性能优化和最佳实践包括使用合适的签名算法、合理设置有效期、

会话劫持可以通过以下步骤实现:1.获取会话ID,2.使用会话ID,3.保持会话活跃。在PHP中防范会话劫持的方法包括:1.使用session_regenerate_id()函数重新生成会话ID,2.通过数据库存储会话数据,3.确保所有会话数据通过HTTPS传输。

PHP8.1中的枚举功能通过定义命名常量增强了代码的清晰度和类型安全性。1)枚举可以是整数、字符串或对象,提高了代码可读性和类型安全性。2)枚举基于类,支持面向对象特性,如遍历和反射。3)枚举可用于比较和赋值,确保类型安全。4)枚举支持添加方法,实现复杂逻辑。5)严格类型检查和错误处理可避免常见错误。6)枚举减少魔法值,提升可维护性,但需注意性能优化。

SOLID原则在PHP开发中的应用包括:1.单一职责原则(SRP):每个类只负责一个功能。2.开闭原则(OCP):通过扩展而非修改实现变化。3.里氏替换原则(LSP):子类可替换基类而不影响程序正确性。4.接口隔离原则(ISP):使用细粒度接口避免依赖不使用的方法。5.依赖倒置原则(DIP):高低层次模块都依赖于抽象,通过依赖注入实现。

在PHPStorm中如何进行CLI模式的调试?在使用PHPStorm进行开发时,有时我们需要在命令行界面(CLI)模式下调试PHP�...

使用PHP的cURL库发送JSON数据在PHP开发中,经常需要与外部API进行交互,其中一种常见的方式是使用cURL库发送POST�...

静态绑定(static::)在PHP中实现晚期静态绑定(LSB),允许在静态上下文中引用调用类而非定义类。1)解析过程在运行时进行,2)在继承关系中向上查找调用类,3)可能带来性能开销。
