web-dev-qa-db-ja.com

コントローラー内から呼び出される機能要求中にクラスをモックするにはどうすればよいですか?

コントローラーからElastic searchを呼び出して検索結果を返すカスタムモジュールに取り組んでいます。結果が正しく表示されることを確認するためだけに、単純なFunctional Php Unitテストを記述しようとしています。

以下は、私の機能的なphpユニットテストコードの簡略版です。

public function testSearchResulsShowUp() {
  $account = $this->drupalCreateUser(['access content']);
  $this->drupalLogin($account);
  $this->drupalGet('/search/term1');
  $this->assertResponse(200);
  $this->assertText('Term 1');
}

これが私のコントローラーの簡易バージョンです:

/**
 * Search controller.
 */
class SearchController extends ControllerBase {

  public function getContent(search) {
    $client = Client::getClient();

    // Client connects to Elastic search and then returns
    // a result.

    ...
    ...

    // Will return Term 1
  }

}

これが私のクライアントクラスの簡略化されたバージョンです:

/**
 * Class Client.
 */
class Client {

  /**
   * Get client.
   */
  public static function getClient() {
    $cluster_id = \Drupal::state()->get('elasticsearch_connector_get_default_connector');
    $elasticsearchCluster = \Drupal::entityManager()->getStorage('elasticsearch_cluster')->load($cluster_id);
    $clientManager = \Drupal::service('elasticsearch_connector.client_manager');
    $client = $clientManager->getClientForCluster($elasticsearchCluster);
    return $client;
  }
}

GetClientメソッドをどうにかしてモックできるようにしたいので、コントローラまたはそれ以降のどこかで呼び出されると、元のメソッドの代わりにモックが呼び出されます。これが本当の単体テストであった場合、私たちはそれを実行できることを知っていますが、これは機能テストなので、どのように行うかわかりません。

3
albertski

テストが作成しているリクエストは完全に新しいPHPプロセスを起動してそのリクエストのコンテンツを配信するため、テストの実行中にメモリ内で行った処理は何も新しいHTTPに反映されません。リクエストプロセス。

幸い、単一のリクエストよりも長い機能テスト内でcanを使用できるHTTPクライアントを作成するためのいくつかのテクニックがあります。この手法では、モジュールが有効になっている間に発生するすべてのリクエストに対して、コンテナ内のHTTPクライアントを置き換えるテストモジュールを作成します。

あなたのテストはそれから:

  1. セットアップ中にモジュールの電源を入れます。
  2. テストの存続期間中、HTTP要求で新しいHTTPクライアントを使用します。
  3. クリーンアップが必要な場合は、モジュールの電源を切ります。

このようなテストモジュールの主要な部分は次のとおりです。

my_module/tests/modules/my_module_http_client/services.yml

services:
  my_module_http_client.http_client:
    decorates: 'http_client'
    class: 'Drupal\my_module_http_client\MockHttpClient'

my_module/tests/modules/my_module_http_client/src/TestHttpClient

<?php

namespace Drupal\my_module_http_client;

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;

/**
 * Test HTTP client for the air quality test.
 */
class TestHttpClient extends Client {

  /**
   * {@inheritdoc}
   */
  public function get($uri, array $options = []) {
    return new Response(200, ['type' => 'text/xml'], file_get_contents(__DIR__ . '/some-test-fixture.xml'));
  }

}

もちろん::getの本体では、好きなロジックをプログラムできます。一般的なパターンは、状態APIを使用して、テストでクライアントの動作を指定できるようにすることです。たとえば、どの応答フィクスチャを返すかを設定するようなものです。

この種の機能テストは、システム全体がエンドツーエンドで機能していることを確認するのに非常に役立ちます。ただし、これを警告します。分離してテストできるコントローラー内のユニットを識別することも重要です。単体テストでは、完全にブートストラップされた実装よりもはるかに高速で、多くのシナリオをテストできることがよくあります。

2
Sam152