ruflin / Elastica

Elastica is a PHP client for elasticsearch
http://elastica.io/
MIT License
2.26k stars 736 forks source link

How to mock elasticsearch responses for unit tests #1769

Open jakxnz opened 4 years ago

jakxnz commented 4 years ago

Problem

I want to get unit test coverage for my own logic that invokes Elastica. How do I mock responses from Elastica to test specific outcomes?

One potential solution

Make a mock transport based on this package's Elastica\Transport\Guzzle transport, and take advantage of the Guzzle handler to mock responses.

MockTransport.php

<?php

namespace App\Tests;

use Elastica\Connection;
use Elastica\Transport\Guzzle;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;

class MockTransport extends Guzzle
{

    /**
     * @var MockHandler
     */
    protected static $mockHandler;

    /**
     * @return MockHandler
     */
    public static function getMockHandler(): MockHandler
    {
        if (!static::$mockHandler) {
            static::$mockHandler = new MockHandler();
        }

        return static::$mockHandler;
    }

    /**
     * {@inheritDoc}
     */
    protected function _getGuzzleClient($baseUrl, $persistent = true)
    {
        return new Client([
            'base_uri' => $baseUrl,
            'handler' => static::getMockHandler(),
        ]);
    }
}

Then you can use the mock transport when you set up your client e.g

    $client = new \Elastica\Client(['transport' => MockTransport::class]);

    MockTransport::getMockHandler()->append(function () {
        return new \GuzzleHttp\Psr7\Response\Response(
            ... // Add your mock Elasticsearch response payload
        );
    });

    $response = $client->request(...); // Add your arguments

    // Make your assertions here...

I have an Injector in my project, so was able to use the injector to assign the MockHandler (during unit tests) to logic that instantiates an Elastica\Client. You may need your own approach to assign the mock handler during unit test execution.

ruflin commented 4 years ago

@jakxnz I must confess, this is not something I was thinking about before. Do you need anything from us or does the above work for you?

jakxnz commented 4 years ago

@ruflin, thanks for engaging in this! This suits our needs, but I am open to more effective solutions if anyone knows them

thePanz commented 4 years ago

@jakxnz : is your logic being part of ES requests? Like a new query type or endpoint? If not, you could mock the ES response, instead of going down to mocking guzzle requests. (my 2 cents)

jakxnz commented 4 years ago

No @thePanz , my logic uses all out-of-the-box types/endpoints. Mocking the ES response sounds like it could be quite clean, do you have an example of how you would do that?