модульное тестирование с DI - PullRequest
3 голосов
/ 22 мая 2011

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

Итак.Внедрить объект или 2 для тривиального класса просто,

Я изучил контейнеры зависимостей, увидел много реализаций и теперь задаюсь вопросом, в чем преимущество использования контейнера по сравнению с реестром, например.Разве нельзя так просто использовать реестр для хранения анонимных функций, которые создают необходимые зависимости при вызове?

2 контейнера, в которые я заглянул, Php Dependency и Pimple сильно отличаются в реализации.

Я задаюсь вопросом о преимуществах пользовательского контейнера по сравнению с проходящими прямыми объектами.Я не понимаю, как будет проверяться реализация php-зависимости, т.е. как реализовать фиктивный объект базы данных в phpunit без фактического внедрения класса при тестировании?Есть ли преимущество в том, чтобы зависимость отображалась и использовалась в doctag вроде этого?

Class Book {

  private $_database;

  /**
   * @PdInject database
   */
  public function setDatabase($database) {
      $this->_database = $database;
  }

}

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

  Objects are defined by anonymous functions that return an instance of the object:

 // define some parameters
 $container['cookie_name'] = 'SESSION_ID';
 $container['session_storage_class'] = 'SessionStorage';

... который может вести себя как фабрика одновременно:

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};

Объявление общих ресурсов всегда обслуживает один и тот же экземпляр (синглтон!?):

$c['session'] = $c->share(function ($c) {
return new Session($c['session_storage']);
});

Это была идея использования простого реестра, который содержит объекты или анонимные функции.Но я что-то упускаю в этом подходе?Прыщ, я вижу, как тестировать, но Php-зависимость мне неясна с точки зрения тестирования.

1 Ответ

2 голосов
/ 22 мая 2011

Обычно в наших приложениях мы внедряем конструктор и определяем интерфейс для всех компонентов нашей системы:

class Book
{
    /**
     * @var Db_AdapterInterface
     */
    private $_database;

    public function __construct(Db_AdapterInterface $database)
    {
        $this->_database = $database;
    }
}

У нас, конечно, есть стандартный Db_Adapter, а затем еще один Db_TestAdapter.В Db_TestAdapter мы можем определить результаты SQL-запросов в наших тестах.

Для нашего обычного приложения у нас есть что-то вроде этого для нашего контейнера:

$container->add('Db_AdapterInterface', new Db_Adapter());

И затем в наших тестах мы имеемвместо этой строки:

$container->add('Db_AdapterInterface', new Db_TestAdapter());

Чтобы получить экземпляр Book, мы просто запрашиваем у него контейнер:

$book = $container->construct('Book');

И контейнер внедряет все необходимые зависимости в объект.

Если все ваши объекты слабо связаны, т. Е. Объекту A нужен только интерфейс B, тогда вы всегда можете предоставить объекту A тестовую реализацию.На этом этапе какой контейнер вы используете, не имеет значения.

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

Обновление: Я добавил пример подключения объектов в контейнере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...