web-dev-qa-db-ja.com

drupal 8でDIを使用してhttpクライアントサービスを単体テストする方法は?

外部ソースからデータを消費するサービスがあります。サービスの単体テストをしたいのですが、テストを設定する方法がわかりません。まず、1つのメソッドをテストしたいと思います。これが私のサービスです:

<?php

namespace Drupal\data_provider;

use Drupal\data_provider\Security\DataProviderCrypt;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\ClientInterface;
use Drupal\Core\Cache\CacheBackendInterface;



/**
 * DataProviderServic.
 *
 * @ingroup data_provider
 *
 * @group data_provider
 */
class DataProviderService {


  public $baseUri;


  protected $username;


  protected $password;

  /**
   * The HTTP client to fetch the feed data with.
   *
   * @var \GuzzleHttp\ClientInterface
   */
  protected $httpClient;

  /**
   * The cache.default cache backend.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cacheBackend;

  /**
   * Constructs a database object.
   *
   * @param \GuzzleHttp\ClientInterface $http_client
   *   The Guzzle HTTP client.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
   *   The cache object associated with the default bin.
   */
  public function __construct(ClientInterface $http_client, CacheBackendInterface $cache_backend) {
    $this->httpClient = $http_client;
    $this->cacheBackend = $cache_backend;
    $this->baseUri = \Drupal::config('data_provider.settings')->get('base_uri');
    $this->username = \Drupal::config('data_provider.settings')
      ->get('username');
    $this->password = \Drupal::config('data_provider.settings')
      ->get('password');
  }


  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    // Forms that require a Drupal service or a custom service should access
    // the service using dependency injection.
    // @link https://www.drupal.org/node/2203931.
    // Those services are passed in the $container through the static create
    // method.
    return new static(
      $container->get('http_client'),
      $container->get('cache.default')
    );
  }


  /**
   * Create a formatted request based on options provided
   *
   * @param $url
   *   Requested URl.
   * @param array $options
   *  Query parameter options.
   * @param bool $reset
   *   Rest the cache.
   *
   * @return bool|string
   */
  public function doRequest($url, $options = array(), $reset = FALSE) {
    // request from api
    return $content;
  }

  /**
   * Return the data from the API in json format.
   *
   * @return bool|mixed
   */
  public function getSubscriptions() {
    // do_request and from the api and return info
    return $subscriptions;
  }



}

そしてこれが私のテストです:

<?php

namespace Drupal\data_provider\Tests\Unit;

use Drupal\Tests\UnitTestCase;
use Drupal\data_provider\DataProviderService;

/**
 * DataProviderServic unit test.
 *
 * @ingroup data_provider
 *
 * @group data_provider
 */
class DataProviderServiceTest extends UnitTestCase {

  /**
   * Very simple test of DataProviderService::getSubscriptions().
   * @todo write dataprovider tests.
   */
  public function testGetSubscriptions() {
    $dp = new DataProviderService();
    $subs = $dp->getSubscriptions();
    $this->assertEquals(TRUE, TRUE);
  }

}

テストを実行すると、次のエラーが発生します。

PHPUnit 4.8.36 by Sebastian Bergmann and contributors.

Testing 
E

Time: 42.35 seconds, Memory: 610.25MB

There was 1 error:

1) Drupal\data_provider\Tests\Unit\DataProviderServiceTest::testGetSubscriptions
Argument 1 passed to Drupal\data_provider\DataProviderService::__construct() must be an instance of GuzzleHttp\ClientInterface, none given, called in /var/www/drupal8/docroot/modules/custom/data_provider/tests/src/Unit/DataProviderServiceTest.php on line 22 and defined

/var/www/drupal8/docroot/vendor/symfony/phpunit-bridge/DeprecationErrorHandler.php:73
/var/www/drupal8/docroot/modules/custom/data_provider/src/DataProviderService.php:63
/var/www/drupal8/docroot/modules/custom/data_provider/tests/src/Unit/DataProviderServiceTest.php:22

この問題はDIと、それを適切に行う方法に関係していると思います。だから私の質問:
1。サービスのテスト方法から始めるにはどうすればよいですか?
2。私のユニットテストは実際のAPIリクエストを行うべきですか、それともモックするべきですか?

4
awm

あなたは正しい軌道に乗っています!

また、テストは外部サービスの呼び出しに依存するべきではありません。外部サービスに期待する応答とシナリオのリストを作成し、テストが常に確定的なソースデータを使用していることを確認する必要があります。

テストでは、依存関係のインスタンスを提供するか、コンストラクターを通じてそれらのモックバージョンを提供する必要があります。 PHPUnitには、依存関係を模擬するいくつかの方法が付属しています。予言は人気のあるオプションです(以下で使用)。コードで使用するのと同じ方法でモックを作成できます。以下は、応答を持つモックHTTPクライアントを作成する例です。

_use GuzzleHttp\ClientInterface;
use Prophecy\Argument;
use GuzzleHttp\Psr7\Response;

$client  = $this->prophesize(ClientInterface::class);
// When the request method is called on the HTTP client, with "GET", a specific
// URL of our choosing and any third argument, make sure the following response
// is sent.
$client->request('GET', 'http://example.com/api-endpoint', Argument::any())->will(function () {
  return new Response(200, [], '[{"valid api response"}]');
});

$dp = new DataProviderService($client->reveal());
_

「フィクスチャ」というフォルダを作成し、APIからの応答を含むファイルの作成を開始できます。 APIが正しく動作しなくなるシナリオを想像してください。不正なJSONを送信し、テストケースを作成して、コードがどのように応答するかを確認してください。少し

注:これは単なる出発点です。クラスをユニットテストするには、キャッシュの依存関係を処理し、「\ Drupal」静的メソッドへの呼び出しを排除する必要があります。\Drupalクラスの各メソッドをクリックして、依存関係としてどのように注入できるかを確認してください。たとえば、_\Drupal::config_は次のように定義されます。

_  public static function config($name) {
    return static::getContainer()->get('config.factory')->get($name);
  }
_

したがって、設定を$container->get('config.factory')->get('data_provider.settings')として挿入することができます。

このような設定を行うことは、常に少し習得することになりますが、その過程で習得するものは非常に貴重であり、持続し続けます!

私はこのセッションをDrupalConウィーンで発表しました。一般的なテストに関するいくつかの有用な情報があるかもしれません: https://www.youtube.com/watch?v=MquqAplUXFY

5
Sam152