How can I mock a class during a functional request that is called from within a controller? I am working on a custom module that from a controller calls Elastic search and returns a search result.  I am trying to write a simple Functional Php Unit test just to make sure the results are displaying correctly.

Here is a simplified version of my funtional php unit test code:

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


Here is a simplified version of my controller:

/**
 * 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
  }

}


Here is a simplified version of my Client class:

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

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


I would like to be able to somehow mock my getClient method so when it is called in the Controller or anywhere going forward it would call the mock instead of the original method.  I know that if this was a true Unit test we would be able to do it but since it is a functional test I am not sure how to do it.
Since the request your test is making spins up a completely new PHP process to deliver the contents of that request, nothing you do in-memory during the execution of the test will be reflected in the new HTTP request process.

Fortunately there are some techniques to create HTTP clients that can be used inside functional tests that span longer than a single request. The technique involves creating a test module, which replaces the HTTP client in the container, for all requests that happen while the module is enabled.

Your test may then:


Switch the module on during setup.
Use the new HTTP client in HTTP requests during the lifetime of the test.
Switch the module off, if you're required to clean-up.


The key parts of a test module like this are:

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

&lt;?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' =&gt; 'text/xml'], file_get_contents(__DIR__ . '/some-test-fixture.xml'));
  }

}


Of course in the body of ::get, you can program any logic you like. A common pattern is to use the state API to allow the test to specify a behavior for the client. An example might be something like, setting which response fixture to return.

These kind of functional tests are really valuable to ensure your whole system is working end to end. I would however caveat this with saying, it is also valuable to identify units in your controller which can be tested in isolation. Unit tests can often test a lot more scenarios at a might higher speed than a fully bootstrapped implementation might be able to.