sellerlabs / injected
自动模拟注入
Requires
- mockery/mockery: 0.9.*
Requires (Dev)
- phpunit/phpunit: ~5.0
This package is not auto-updated.
Last update: 2024-09-22 03:22:28 UTC
README
自动模拟依赖注入以进行测试
这是什么?
InjectedTrait
允许您轻松创建具有所有依赖项模拟的类,用于测试目的。
为什么?
以下模式非常常见
- 创建一个类
A
,其依赖项(服务对象)通过构造函数传入 - 在对象的内部各种函数中使用这些服务
- 创建测试来模拟
A
的每个依赖项,断言它们按预期调用。
大量的测试逻辑最终变成了构建对象时的样板代码。 InjectedTrait
旨在完全删除这些样板代码,让您专注于真正重要的事情:测试本身。
入门
使用 composer
获取最新版本(您可能只需要在开发中使用它)
$ composer require sellerlabs/injected --dev
示例用法
假设我们正在开发一个Web应用程序,并且当用户注册时想要向他们发送电子邮件。为了简单起见,让我们假设用户完全由他们的电子邮件地址定义。当他们注册时,我们自然希望发送感谢电子邮件。此外,我们希望测试电子邮件是否真的被发送了 而不实际发送它们。
首先,让我们定义一个电子邮件服务
class EmailService { public function email($address, $content) { // Send an email to $address with body $content } }
(在实际应用程序中,email
会发送电子邮件 -- 我们这里不关心实现细节!)
让我们还定义一个 UserController
,它处理非常简单的注册过程
class UserController { private $service; public function __construct(EmailService $service) { $this->service = $service; } public function signUp($emailAddress) { $this->service->email($emailAddress, 'Thanks for signing up!'); return $emailAddress; } }
在这里,我们通过构造函数提供 EmailService
依赖项,并在我们的(极其简单)注册过程中使用它。
为了测试这个类,我们必须做以下两件事之一
- 实际发送一封电子邮件,并确保以某种方式发送了,或者
- 使用类似
Mockery
的工具模拟EmailService
对象,并确保email
以预期的参数被调用。
InjectedTrait
允许您轻松实现选项2。让我们看看
use SellerLabs\Injected\InjectedTrait; /** * Class InjectedExample * * // 1. These are helpful annotations for IDEs and language tools * @property MockInterface $service * @method UserController make() * * @author Benjamin Kovach <benjamin@roundsphere.com> */ class InjectedExample extends PHPUnit_Framework_TestCase { // 2. Use our trait use InjectedTrait; // 3. Provide the name of the class to test protected $className = UserController::class; public function testSignUp() { // 4. Make a controller with mocked dependencies $controller = $this->make(); $address = 'email@test.me'; // 5. We can access any mocked dependency of the class as a property $this->service->shouldReceive('email') ->withArgs( [ $address, 'Thanks for signing up!' ] ); $result = $controller->signUp($address); $this->assertEquals($address, $result); } }
使用 InjectedTrait
的每个类都必须有一个 $className
属性,该属性用于定位正在测试的类。 InjectedTrait
提供一个公共方法,make
,该方法构建此类类型的对象,但模拟其依赖项并将其作为属性保存到测试类本身。
因此,在 testSignUp
中,我们使用 make()
构建控制器,这使我们能够访问一个名为 $service
的模拟 EmailService
类型对象。这是因为它在 UserController
的构造函数中这样定义的。
public function __construct(EmailService $service) { $this->service = $service; }
在测试用例期间,$service
成员变量绑定到这个模拟的 EmailService
,这允许我们预测当控制器中的 signUp
方法被调用时它会发生什么。我们使用 Mockery
来创建模拟对象。类注释中有一些注释,有助于IDE自动完成这些类,因为模拟属性是动态声明的。
这个例子在 tests/InjectedExample.php
中。请随意探索!
此特性的影响可能看起来相对较小,但在大型应用程序中,当类有多个依赖项时,这使得测试变得 容易得多。