2

I am trying to use BrowserTestBase to write a functional test for a Form. The form populates some form elements with data retrieved from an external site. The client for this external site is injected into the form as a Drupal 8 service. The client uses Guzzle, and I'm building the mock client using code similar to the following one.

$myClient = new MyClient(Drupal::configFactory(), [
  'handler' => HandlerStack::create(
    new MockHandler([
      new Response(200, ['X-Foo' => 'Bar'], 'Hello, World')
    ])
  ),
  'base_uri' => 'http://example.com/api/',
]);

I've tried doing this in the test class to assign my mock client to the service.

$this->container->set('my.service', $mockClient);

I've tried placing this assignment in setUp() as well as the testMyForm() method itself, but the container passed to the form's create() method doesn't contain $mockClient.

Update:

After digging into the code I discovered that parameters and services may be initialized in the test-site context by adding them inside testing.services.yml and placing that in the default site folder. In my test I used:

services:
  browser_test.handler_stack:
    class: GuzzleHttp\HandlerStack
    factory: GuzzleHttp\HandlerStack::create
    arguments:
      - '@browser_test.mock_handler'
  browser_test.mock_handler:
    class: GuzzleHttp\Handler\MockHandler
  my.service:
    class: Drupal\mymodule\Client\MyClient
    arguments:
      - '@config.factory'
      -
        base_uri: 'http://localhost/test/'
        handler: '@browser_test.handler_stack'

This overrode the client (which solves part of the problem), however I'm still unable to inject mock responses into MockHandler from the test class. I tried:

/** @var MockHandler $handlerStack */
$handler = $this->container->get('browser_test.mock_handler');
$handler->append(new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'));

$this->drupalLogin($this->adminUser);
$this->drupalGet('path/to/my_form');
$this->assertSession()->statusCodeEquals(200);

But MockHandler is throwing an exception because Mock queue is empty.

  • I also was trying this earlier in the week. The docs state that this is how you'd override it, but I was not seeing that to be the case. Interested in any answers. – Kevin Dec 13 '19 at 20:44
  • 1
    For reference... drupal.stackexchange.com/questions/289129/… – Kevin Dec 14 '19 at 0:37
  • It looks like params and services can be initialized by adding a sites/default/testing.services.yml file to the site. This file gets copied to the site provisioned for the functional test (see FunctionalTestSetupTrait::prepareSettings(). However it's still unclear if it's possible to inject mock responses to Guzzle requests from the Test class to the test site. – J Webb Dec 14 '19 at 5:50
  • 4
    Does this answer your question? Mock a service while running a BrowserTest? – leymannx Dec 14 '19 at 16:18
  • I it is not possible to modify the guzzle options after the guzzle client has been initiated. This isn't necessarily a duplicate, but I think overriding the output of the service's method like @leymannx suggests is appropriate rather than try to test the internals of Guzzle. – mradcliffe Dec 16 '19 at 20:35