Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking multiple ES responses #190

Open
a-drew opened this issue Apr 12, 2023 · 2 comments
Open

Mocking multiple ES responses #190

a-drew opened this issue Apr 12, 2023 · 2 comments

Comments

@a-drew
Copy link
Contributor

a-drew commented Apr 12, 2023

I've found an issue while setting up tests and trying to retrieve multiple responses after mocking the results. No matter how you set it up, your response will be provided only the first time, any additional requests to search will fail with the following error: ErrorException : Undefined array key "hits"

Here's example of a failing test:

    public function it_can_lazy_map_results_into_multiple_models(): void
    {
        $file = Storage::disk('test')->readStream('elastic-response-multi-index.json');
        $this->swap(
            ElasticClientFactory::class,
            ElasticClientFactory::fake(new FakeResponse(200, $file))
        );
        $models = Post::search('abc')->join(Media::class)->cursor(); // the es response matches my file
        $this->assertCount(2, $models->where(fn($model) => $model instanceof Post));
        $this->assertCount(2, $models->where(fn($model) => $model instanceof Media));

        $models = Media::search('abc')->join(Post::class)->cursor(); //the es response is completely empty
        $this->assertCount(2, $models->where(fn($model) => $model instanceof Media));
        $this->assertCount(2, $models->where(fn($model) => $model instanceof Post));
    }

After a bit of digging I found this to be a known issue upsteam (elastic/elasticsearch-php#1043), however I did find this workaround:

$this->partialMock(DocumentAdapterInterface::class)
    ->allows('search')
    ->andReturns(new Results([
        'took' => 28,
        'timed_out' => false,
        '_shards' => ['total' => 1, 'successful' => 1, 'skipped' => 0, 'failed' => 0],
        'hits' => ['total' => ['value' => 0], 'max_score' => null, 'hits' => [
            // your results go here
        ]],
    ]));

...

If you find it useful, this could be added to the docs or even replace the current file based approach.

@Jeroen-G
Copy link
Owner

It depends on what you want to test. I like the file based way because it is an actual ES response, so we can't make the wrong assumptions about it. In other scenarios I can imagine you just want the adapter to return a certain set of assumed results.

For now I think this issue will serve as good enough documentation until more people raise similar issues. Thanks for investigating and your elaborate description (as always 😄)!

@a-drew a-drew changed the title Mocked ElasticClient cannot be used twice Mocking multiple ES responses Apr 13, 2023
@a-drew
Copy link
Contributor Author

a-drew commented Apr 13, 2023

This sounds good to me, most of my tests worked fine with the file approach this was really an outlier.

I'll also note that its possible to use files in this workaround, albeit you're just passing json around and skip both guzzle and the whole ES client mock.

$file = Storage::disk('test')->get('elastic-response-multi-index.json');

$this->partialMock(DocumentAdapterInterface::class)
    ->allows('search')
    ->andReturns(new Results(json_decode($file, true)));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants