Интеграционные тесты макетов фасадов против инъекционных макетов

У нас есть несколько устаревших проектов 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();

В прежних системах это казаться что фиктивный фасад является «глобальным», так что когда фасад запускается в каждом классе, точно такое же значение возвращается.

Правильно ли я думаю об этом?

*Я понимаю, что этот пример может показаться полностью избыточным с точки зрения архитектуры кода и тестирования, но я убираю всю реальную функциональность, чтобы попытаться привести своего рода «простой» пример того, что я спрашиваю.

14

Решение

Инъекция зависимости от фасадов

Одним из основных преимуществ внедрения зависимостей является то, что код становится намного более тестируемым, когда вы начинаете внедрять зависимости в методы, а не создавать их / жестко кодировать их внутри метода. Это потому, что вы можете передавать зависимости изнутри модульных тестов, и они будут распространяться через код.

Увидеть: http://slashnode.com/dependency-injection/

Внедрение зависимостей резко контрастирует с фасадами. Фасады являются статическими глобальными классами, язык PHP не позволяет перезаписывать или заменять статические функции на статических классах. Фасады Laravel используют Mockery для обеспечения имитационной функциональности, и они ограничены теми же фактами, что и выше.

Проблема для интеграционного тестирования может возникнуть, когда вы надеетесь получить данные из немоделированного кэша, но как только вы используете Facade :: shouldReceive (), тогда Facade :: get () будет переопределен поддельным кэшем. Обратное также верно. В результате, фасады не подходят, когда вы чередуете вызовы для поддельных и немодированных данных.

Чтобы протестировать ваш код с различными наборами данных, которые вам требуются, рекомендуется реорганизовать ваш прежний код для использования DI.

Интеграционные тесты

Более простой метод

Альтернативой является вызов нескольких Facade :: shouldReceive () с ожиданиями в начале вашего интеграционного теста. Убедитесь, что у вас есть правильные числа ожиданий в правильном порядке для каждого из вызовов, которые вы сделаете в интеграционном тесте. Вероятно, это был бы более быстрый способ написания тестов с учетом существующей кодовой базы.

Более сложный метод

В то время как внедрение зависимостей является наилучшей практикой программирования. Вполне может быть, что ваша кодовая база имеет так много унаследованных классов, что на рефакторинг потребуется невероятное количество времени. В этом случае, возможно, стоит рассмотреть сквозные интеграционные тесты с использованием тестовой базы данных с фиксаторами.

Приложение:

6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]