Выполнение модульного тестирования с вложенными зависимостями и заводскими классами

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

До сих пор я полагаю, что я понимаю теорию модульного тестирования, но мне было интересно, в сценарии, когда один делегат обрабатывает вложенные зависимости объектов к Factory, как следует идти об одном модульном тестировании Factory, или просто избыточно тестировать его ? И каков наилучший подход к тестированию того, что «цепочка» зависимостей хорошо работает в синхронизации?

Позвольте мне проиллюстрировать вопросы. Предположим, у вас есть следующий «устаревший» код:

class House { protected $material; protected $door; protected $knob; public function __construct() { $this->door = new Door(); $this->knob = $this->door->getKnob(); $this->material = "stone"; echo "House material: ".$this->material . PHP_EOL . "<br/>"; echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>"; echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>"; } } class Door { protected $material; protected $knob; public function __construct() { $this->knob = new Knob(); $this->material = "wood"; } public function getKnob() { return $this->knob; } public function getMaterial () { return $this->material; } } class Knob { protected $material; public function __construct() { $this->material = "metal"; } public function getMaterial () { return $this->material; } } $house = new House(); 

Это (насколько я понимаю) плохо для модульного тестирования, поэтому мы заменяем жестко закодированные зависимости DI + классом Factory:

 class House { protected $material; protected $door; protected $knob; public function __construct($door) { $this->door = $door; $this->knob = $this->door->getKnob(); $this->material = "stone"; echo "House material: ".$this->material . PHP_EOL . "<br/>"; echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>"; echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>"; } } class Door { protected $material; protected $knob; public function __construct($knob) { $this->knob = $knob; $this->material = "wood"; } public function getKnob() { return $this->knob; } public function getMaterial () { return $this->material; } } class Knob { protected $material; public function __construct() { $this->material = "metal"; } public function getMaterial () { return $this->material; } } class HouseFactory { public function create() { $knob = new Knob(); $door = new Door($knob); $house = new House($door); return $house; } } $houseFactory = new HouseFactory(); $house = $houseFactory->create(); 

Теперь (и опять же, насколько я понимаю) House, Door and Knob могут быть проверены с помощью смехотворных зависимостей. Но:

1) Что происходит с HouseFactory сейчас?

Если нужно просто:

  • Не тестируйте его, так как он еще не тестировал какую-либо прикладную логику, и на самом деле Фабрики остаются такими. Предположим, что если независимые тесты для House, Door & Knob проходят Factory, все должно быть хорошо.
  • Рефакторинг фабрики каким-то образом, т. Е. Использование функций внутри класса, чтобы получить каждый экземпляр таким образом, чтобы можно было переопределить эти функции через PHPUnit, чтобы возвращать макетные объекты, на всякий случай, если в классе есть какая-то дополнительная логика, которая могла бы использовать некоторое тестирование в будущее.

2) Можно ли настроить тесты, которые полагаются на несколько (не издевающихся) зависимостей сразу? Я понимаю, что это технически не модульное тестирование (возможно, интеграционное тестирование?), Но я полагаю, что это все еще отлично справляется с использованием PHPUnit? Учитывая приведенный выше пример, я хотел бы иметь возможность настроить тест, который не только изолирует House, Door, Knob и HouseFactory, но и результаты взаимодействия реальных объектов друг с другом, возможно, с некоторыми из них функции издеваются, например те, которые относятся к данным. Является ли PHPUnit плохим выбором для такого рода тестов?

Спасибо заранее за ваше время. Я понимаю, что некоторые из допущений, которые я делаю, могут быть неверными, поскольку я, очевидно, не специалист по этому вопросу; исправления приветствуются и оцениваются.

Завод похож на new ключевое слово. Вы тестируете new ключевое слово? Нет, вы проверяете, можете ли вы построить класс. Но это не зависит от самого завода и части устройства, которое уже является частью ваших модульных тестов.

2) называется интеграционным тестированием. И вы можете сделать это с помощью PHPUnit.


Изменить – поскольку в комментариях было некоторое обсуждение:

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

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

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

И я не хотел создавать впечатление, что другие ваши подразделения должны иметь жестко запрограммированные вызовы на фабричный метод (ы) создания вместо того, чтобы вводить вложенную зависимость. Поскольку вы не должны использовать new внутри своих устройств, вы также не должны использовать Factory::create . Подобно new , имя класса ( Factory ) жестко закодировано, а не вводится. Тогда это скрытая зависимость. Но зависимости не следует скрывать; но сделал видимым.

Вы можете проверить его с наследованием.

Просто дополните House с помощью FakeHouse для тестирования, а затем проверьте $ material, $ door и $ knob и т. Д., Изменились ли они или нет после тестирования.