Автоматическое внедрение ложных зависимостей для тестирования
injected Trait
позволяет вам легко создавать классы со всеми их зависимостями, макетируемыми для целей тестирования.
Очень распространена следующая закономерность:
A
с его зависимостями (объектами службы), переданными через конструктор.A
, утверждая, что они вызываются так, как ожидалось. Большая часть логики тестирования в конечном итоге становится шаблоном для построения объекта. injected Trait
направлен на полное устранение этого шаблона, позволяя вам сосредоточиться на том, что важно: на самих тестах.
Используйте composer
, чтобы получить последнюю версию (вероятно, он понадобится вам только для разработки):
$ composer require sellerlabs/ injected --dev
Предположим, мы разрабатываем веб-приложение и хотим отправлять пользователям электронное письмо при регистрации. В качестве простого примера предположим, что пользователь полностью определяется своим адресом электронной почты. Когда они регистрируются, мы, естественно, хотим отправить им письмо с благодарностью. Кроме того, мы хотели бы проверить, действительно ли электронные письма отправляются, но не отправляются на самом деле .
Сначала давайте определим службу электронной почты:
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
через конструктор и используем ее во время нашего (невероятно простого) процесса регистрации.
Чтобы протестировать этот класс, нам придется сделать одно из двух:
EmailService
, используя что-то вроде Mockery
, и убедитесь, что email
вызывается с ожидаемыми аргументами. injected Trait
позволяет безболезненно достичь варианта 2. Давайте посмотрим:
use SellerLabs injected injected Trait ;
/**
* Class injected Example
*
* // 1. These are helpful annotations for IDEs and language tools
* @property MockInterface $service
* @method UserController make()
*
* @author Benjamin Kovach <[email protected]>
*/
class injected Example extends PHPUnit_Framework_TestCase
{
// 2. Use our trait
use injected Trait;
// 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 protected] ' ;
// 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 );
}
}
Каждый класс, использующий injected Trait
, должен иметь свойство $className
, которое используется для поиска тестируемого класса. injected Trait
предоставляет единственный общедоступный метод make
, который конструирует объект этого типа, но имитирует его зависимости и сохраняет их как свойства самого тестового класса.
Итак, в testSignUp
мы создаем контроллер с помощью make()
, который дает нам доступ к имитированному объекту типа EmailService
с именем $service
. Это потому, что это определено в конструкторе UserController
:
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
На время тестового примера переменная-член $service
привязана к этому имитированному EmailService
, что позволяет нам делать предположения о том, что произойдет с ней, когда будет вызван метод signUp
контроллера. Мы используем Mockery
для создания издеваемых объектов. В комментарии к классу есть несколько аннотаций, которые помогают при автодополнении этих классов в IDE, поскольку фиктивные свойства объявляются динамически.
Этот пример находится в tests/ injected Example.php
. Не стесняйтесь ковыряться!
Влияние этой особенности может показаться относительно небольшим, но при работе с большими приложениями, где классы имеют несколько зависимостей, это значительно упрощает тестирование.