Интеграционные тесты насмешливых фасадов и инъекций
У нас есть некоторые старые проекты laravel, которые используют фасады в классах.
use Cache;
LegacyClass
{
public function cacheFunctionOne()
{
$result = Cache::someFunction('parameter');
// logic to manipulate result
return $result;
}
public function cacheFunctionTwo()
{
$result = Cache::someFunction('parameter');
// different logic to manipulate result
return $result;
}
}
Наши более поздние проекты используют инъекцию зависимостей лежащих в основе классов laravel, которые представляют собой фасады, как это было намечено самим Тейлором Отуэлом. (Мы используем инъекцию конструктора для каждого класса, но чтобы сохранить пример короче, здесь я использую метод инъекции и использую один класс.)
use Illuminate\Cache\Repository as Cache;
ModernClass
{
public function cacheFunctionOne(Cache $cache)
{
$result = $cache->someFunction('parameter');
// logic to manipulate result
return $result;
}
public function cacheFunctionTwo(Cache $cache)
{
$result = $cache->someFunction('parameter');
// different logic to manipulate result
return $result;
}
}
Я знаю, что фасады могут быть издевались
public function testExample()
{
Cache::shouldReceive('get')
->once()
->with('key')
->andReturn('value');
$this->visit('/users')->see('value');
}
Что хорошо работает для модульных тестов. Проблема, которую я пытаюсь понять, заключается в том, что эти фасады высмеиваются "глобально".
Например, представьте себе, что я пишу интеграционный тест (тестирование нескольких взаимосвязанных классов, в то время как насмешливые сервисы - не тест конца к концу с использованием живых сервисов), который в какой-то момент выполняет два отдельных класса , которые содержат одинаковые который вызывает тот же метод с теми же параметрами.
В промежутке между этими классами, вызываемыми, является некоторая сложная функциональность, которая изменяет, какие данные возвращаются этим методом фасадов, используя тот же самый параметр. *
$modernClass->cacheFunctionOne($cache); // easily mocked
// logic that changes data returned by laravel Cache object function 'someFunction'
$modernClass->cacheFunctionTwo($cache); // easily mocked with a different mock
Наши современные классы легко тестируются, потому что базовый класс, который представляет собой фасад, вводится в каждый класс (в этом примере каждый метод). Это означает, что я могу создать два отдельных макета и ввести их в каждый класс (метод), чтобы высмеять разные результаты.
$legacyClass->cacheFunctionOne();
// logic that changes data returned by laravel Cache object function 'someFunction'
$legacyClass->cacheFunctionTwo();
В старых системах, однако, кажется, что издевательский фасад является "глобальным", так что, когда фасад запускается в каждом классе, возвращается то же самое значение.
Я правильно понял это?
* Я понимаю, что этот пример может казаться полностью избыточным из архитектуры кода и точки тестирования, но я убираю все реальные возможности, чтобы попытаться дать какой-то "простой" пример того, что я прошу.
Ответы
Ответ 1
Инъекция зависимостей против фасадов
Одним из основных преимуществ Injection Dependency является то, что код становится намного более проверяемым, как только вы начинаете вводить зависимости в методы вместо того, чтобы создавать/кодировать их внутри метода. Это связано с тем, что вы можете проходить в зависимостях изнутри модульных тестов, и они будут распространяться через код.
Смотрите: http://slashnode.com/dependency-injection/
Инъекция зависимостей резко контрастирует с фасадами. Фасады представляют собой статические глобальные классы, язык PHP не позволяет перезаписывать или заменять статические функции на статические классы. Фасад Laravel использует Mockery для обеспечения макетной функциональности, и они ограничены теми же фактами, что и выше.
Проблема для тестирования интеграции может возникнуть там, где вы надеетесь извлечь данные из неизмеримого кеша, но как только вы используете Facade:: shouldReceive(), то Facade:: get() будет переопределяться издеваемым кешем. Обратное также верно. В результате, Фасады не подходят, если вы чередуете вызовы для издевающихся и незафиксированных данных.
Чтобы проверить свой код с различными наборами данных, которые вам нужны, наилучшей практикой было бы реорганизовать ваш устаревший код на использование DI.
Интеграционные тесты
Простой метод
Альтернативой является вызов нескольких Facade:: shouldReceive() с ожиданиями в начале вашего теста интеграции. Обеспечение того, чтобы у вас было правильное количество ожиданий в правильном порядке для каждого из вызовов, которые вы будете делать в тесте интеграции. Вероятно, это был бы более быстрый способ записи тестов с учетом существующей кодовой базы.
Более жесткий метод
В то время как инъекция зависимостей - это наилучшая практика программирования. Вполне возможно, что ваша кодовая база имеет так много классов наследия, что для рефакторинга потребуется невероятное количество времени. В этом случае, возможно, стоит рассмотреть сквозные интеграционные тесты, используя тестовую базу данных с приборами.
Приложение: