Mocking Search Results in new Java API

Are there any examples of how to mock an Elasticsearch search result for the Java API for unit tests with Mockito? Do you mock the entire search result or individual hits? If I search the Internet for examples I only see the old Rest Client examples. Can you use an input stream to manually create your search response?

Someone has previously asked here, but there was no answer to their question. There are no examples that show how to create a manual SearchResponse<> that I can find.

Surely somebody has done this before. It was not that difficult to do in the old REST client, but the SearchResponse structure is different now. I've tried something like:

        ElasticsearchClient client = mock(ElasticsearchClient.class);
        // Set up mocked results
        var response = new SearchResponse.Builder().build().hits().hits().add(0,
                new ESMetrics("aln-nbadev4.labs.server.com", NAME_FIELD_VALUE, SPARK_MASTER_VALUE,
                        "aln-nbadev4.labs.server.com", 0L));

However, I get: "MissingRequiredProperty 'SearchResponse.took'". The idea is to mock in a way similar to this:

        when(client.search((SearchRequest) any(), ESMetrics.class)).
                thenReturn(response);

OK, I think that I've figured this out. I'm posting this here for future reference.

    SearchResponse<ESMetrics> createMockedResponse (long value) {

        return SearchResponse.of(r -> r
                .took(0)
                .timedOut(false)
                .hits(h -> h
                        .total(t -> t.value(1).relation(TotalHitsRelation.Eq))
                        .hits(hit -> hit
                                .id("1234")
                                .index("metrics-7.17.9-2023.07.06")
                                .fields(Map.of("hostname", JsonData.of("aln-nbadev4.labs.server.com"),
                                        "@timestamp", JsonData.of("2020-09-17T20:23:55.382Z"),
                                        "field_name", JsonData.of("string_value"),
                                        "value", JsonData.of(value))))
                )
                .shards(s -> s
                        .total(1)
                        .failed(0)
                        .successful(1)
                )
        );

1 Like

The example above did not completely work because a call to .source() always returned null. You have to define the .source() structure also:

    SearchResponse<ESMetrics> createMockedResponse(long value) {

        ESMetrics entry = new ESMetrics("aln-nbadev4.labs.server.com",
                NAME_FIELD_VALUE, SOURCE_FIELD_VALUE);
        entry.setValue(value);

        return SearchResponse.of(r -> r
                .took(0)
                .timedOut(false)
                .hits(h -> h
                        .total(t -> t.value(1).relation(TotalHitsRelation.Eq))
                        .hits(hit -> hit
                                .id("1234")
                                .index("metrics-7.17.9-2023.07.06")
                                .fields(Map.of("hostname", JsonData.of("aln-nbadev4.labs.server.com"),
                                        "@timestamp", JsonData.of("2020-09-17T20:23:55.382Z"),
                                         NAME_FIELD_NAME, JsonData.of(NAME_FIELD_VALUE),
                                        SOURCE_FIELD_NAME, JsonData.of(SOURCE_FIELD_VALUE),
                                        VALUE_FIELD, JsonData.of(value)))
                                .source(entry))
                )
                .shards(s -> s
                        .total(1)
                        .failed(0)
                        .successful(1)
                )
        );
    }

Then you can mock it like so:

        SearchResponse<ESMetrics> searchResponse = createMockedResponse(1);
        ElasticsearchClient client = mock(ElasticsearchClient.class);
        when(client.search((Function<Builder, ObjectBuilder<SearchRequest>>) any(), any())).thenReturn((SearchResponse) searchResponse);

It is also very important when defining your class that holds the Elasticsearch data that you are reading that if you are using Lombock notations you must use @NoArgsConstructor if any constructors are defined:

    @Data
    @RequiredArgsConstructor
    @NoArgsConstructor(force=true)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class ESMetrics {

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.