Рейтинг:1

Как мне издеваться над \Drupal::httpClient()?

флаг jp

Я модульно тестирую служебную библиотеку (нашей собственной разработки), которая делает вызовы внешнего REST API с помощью \Друпал::httpClient()

Итак, у меня есть класс библиотеки со статическими функциями:

класс myUtils {
  общедоступная статическая функция getFromApi($path)
  {
    ...
    $response = \Drupal::httpClient()->request( ... );
    ...
  }

...

}

и я хочу вызвать это из тестового класса:

класс myUtilsTest расширяет \Codeception\Test\Unit
{
  // Много чего еще...
  публичная функция testGetFromApi()
  {
     // Делаем какую-то магию издевательства
     ...
     myUtils::getFromApi('/some/test/path');
     ...
  }
}

Я понимаю, что могу сделать \GuzzleHttp\Handler\MockHandler и настроить его так, чтобы он возвращал все, что я хочу, но как мне настроить его на «перезапись» вызова \Drupal->httpClient()?

Я видел несколько примеров, которые, кажется, предполагают, что у вас есть экземпляр класса, который имеет свой собственный HTTP-клиент член, и это легко издеваться - но в моем случае использования вообще нет причин проектировать утилиты таким образом. Итак, как мне издеваться над Глобальный \Друпал::httpClient() в таком случае?

Заранее спасибо за любой ответ.

Kevin avatar
флаг in
Проще просто включить тестовый модуль, который имеет промежуточную службу для предоставления ответов для любого внутреннего или внешнего API. В противном случае вам придется имитировать класс, внедренный в тестируемую вами службу, и имитировать каждый метод, который используется в вашей реализации.
флаг in
Это именно тот случай, когда вам нужно внедрение зависимостей, чтобы API-интерфейсы, от которых вы зависите, были слабо связаны с вашим кодом и могли быть смоделированы и заменены во время тестирования. Как только вы начнете зависеть от таких вещей, как HTTP API, API базы данных, API сущностей и друзей, вы, возможно, захотите вместо этого рассмотреть возможность включения своих инструментов в класс обслуживания. Я бы зарезервировал статические функции для автономной логики (например, основные Html.php, Xss.php, UrlHelper.php и т. д. являются хорошими примерами).
флаг jp
Ок, спасибо, будем считать! Тем не менее, необходимость изменять совершенно правильную архитектуру и реализацию исключительно для целей тестирования меня немного раздражает. Но да, это может быть способом.
Kevin avatar
флаг in
Использование статического контейнера в классах не рекомендуется по нескольким причинам, это одна из них. К счастью, сделать DI не так уж и сложно.
jbarrio avatar
флаг cn
Я согласен с Патриком и, пытаясь внести положительный вклад в это, я бы сказал, что вы только что нашли что-то, что можно улучшить в вашей текущей реализации. И, возможно, причина, по которой вы столкнулись с этой проблемой, связана с тем, что вы не реализовали свое решение с учетом подхода T(B)DD с самого начала. В следующий раз с вами такого больше не повторится :_)
флаг jp
Я полагаю, у вас все в порядке! Мой собственный опыт тестирования в основном связан с Python, где можно имитировать несколько более динамично, так что фактический дизайн кода не должен учитывать тестирование (поскольку вы всегда можете протестировать). Это, конечно, Drupal и PHP, а не Python и Django, в которых заключается мой основной опыт. :-)
Рейтинг:3
флаг ph

Преобразование вашего класса для использования внедрения зависимостей будет выглядеть примерно так:

используйте Drupal\Core\Plugin\ContainerFactoryPluginInterface;
используйте Symfony\Component\DependencyInjection\ContainerInterface;
используйте GuzzleHttp\Client

класс myUtils реализует ContainerFactoryPluginInterface {

  /**
   * HTTP-сервис.
   *
   * @var\GuzzleHttp\Клиент
   */
  защищенный $httpClient;

  окончательная публичная функция __construct (массив $configuration, $plugin_id, $plugin_definition, клиент $httpClient) {
    $this->httpClient = $httpClient;
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  общедоступная статическая функция create(ContainerInterface $container, массив $configuration, $plugin_id, $plugin_definition) {
    вернуть новую статику(
      $ конфигурация,
      $plugin_id,
      $plugin_definition,
      $контейнер->получить('http_client')
    );
  }

  публичная функция getFromApi($path)
  {
    ...
    $response = $this->httpClient->request( ... );
    ...
  }

...

}

Ответить или комментировать

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