PHP测试中的Mock对象和Fixtures:如何不疯掉地编写单元测试

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么没有mock对象的测试就像做饭不放盐

\n\n

想象一下,您正在编写一个发送电子邮件通知、操作数据库并调用第三方API的应用程序。如何在没有PHP中的mock对象和fixtures的情况下测试它?您将每次发送真实的邮件,在数据库中创建测试记录,并等待外部服务的响应。这很慢、不可靠,而且很愚蠢。

\n\n

这时,test doubles——真实对象的替代品——就派上了用场。在PHP生态系统中,我们使用以下组合:mock对象用于检查交互,fixtures用于准备数据。让我们看看这在实践中是如何工作的。

\n\n

Mock vs Stub vs Fake:不要混淆概念

\n\n

在深入代码之前,让我们先厘清概念。在PHP单元测试的世界中,有三种主要的替代品类型:

\n\n
    \n
  • Stub(桩)——返回预先定义的数据。您测试您的代码是否正确处理了这些数据。
  • \n
  • Mock(模拟)——不仅返回数据,还检查您的系统如何调用方法:调用次数、参数、顺序。
  • \n
  • Fake(伪造)——一个简化的工作实现(例如,内存数据库)。
  • \n
\n\n

在PHPUnit(PHP测试的事实标准)中,这些概念经常被混淆。但我们要精确:PHP中的mock对象是指您设置期望(expectations)的那些。

\n\n

在PHPUnit中编写第一个mock对象

\n\n

假设我们有一个发送邮件的类Mailer。我们想验证注册服务是否恰好调用了一次send()方法,并且参数正确。

\n\n
<?php\nuse PHPUnit\\Framework\\TestCase;\n\nclass UserRegistrationTest extends TestCase\n{\n    public function testSendWelcomeEmailOnRegistration(): void\n    {\n        // 为Mailer创建mock\n        $mailer = $this->createMock(Mailer::class);\n        \n        // 设置期望:send方法将被调用1次\n        $mailer->expects($this->once())\n               ->method('send')\n               ->with(\n                   $this->equalTo('user@example.com'),\n                   $this->stringContains('Welcome')\n               );\n        \n        $service = new UserRegistrationService($mailer);\n        $service->register('user@example.com', 'password123');\n    }\n}\n
\n\n

注意:我们不检查邮件是否真的发送了。我们检查的是我们的代码尝试发送了它——这就是PHP测试中mock对象的本质。如果send()方法没有被调用(或被调用了两次),测试将失败。

\n\n

expects方法及其功能

\n\n

PHPUnit提供了丰富的API来设置期望:

\n\n
    \n
  • $this->once()——恰好调用一次
  • \n
  • $this->exactly(3)——恰好调用三次
  • \n
  • $this->atLeastOnce()——至少调用一次
  • \n
  • $this->never()——方法不应被调用
  • \n
\n\n

对于参数,使用匹配器(constraints):

\n\n
$mailer->expects($this->once())\n       ->method('send')\n       ->with(\n           $this->callback(function($email) {\n               return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;\n           }),\n           $this->anything() // 第二个参数我们无所谓\n       );\n
\n\n

PHP中的Fixtures:在需要之前准备好数据

\n\n

Fixtures是测试执行前系统所处的预定义数据状态。在PHPUnit中,使用setUp()和

博客

书籍推荐