Автоматическое внедрение ложных зависимостей для тестирования
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 . Не стесняйтесь ковыряться!
Влияние этой особенности может показаться относительно небольшим, но при работе с большими приложениями, где классы имеют несколько зависимостей, это значительно упрощает тестирование.