How to write a proper unit test for Elasticsearch in Java

Viewed 4980

Overview:

I'm totally new to Elastic search testing and I'm gonna add proper unit tests. The project compatibilities are as follow:

  • Java 8
  • Elasticsearch 6.2.4
  • Project uses low level rest client for fetching data from ES

More info about ES configurations is as follow:

import static java.net.InetAddress.getByName;
import static java.util.Arrays.stream;

import java.net.UnknownHostException;
import java.util.Map;
import java.util.Objects;

import javax.inject.Inject;

import org.apache.http.HttpHost;

import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import au.com.api.util.RestClientUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class ElasticConfiguration implements InitializingBean{

    @Value(value = "${elasticsearch.hosts}")
    private String[] hosts;

    @Value(value = "${elasticsearch.httpPort}")
    private int httpPort;

    @Value(value = "${elasticsearch.tcpPort}")
    private int tcpPort;

    @Value(value = "${elasticsearch.clusterName}")
    private String clusterName;

    @Inject
    private RestClientUtil client;

    @Bean
    public RestHighLevelClient restHighClient() {

        return new RestHighLevelClient(RestClient.builder(httpHosts()));
    }

    @Bean
    @Deprecated
    public RestClient restClient() {

        return RestClient.builder(httpHosts()).build();

    }

    /**
     * @return TransportClient
     * @throws UnknownHostException
     */
    @SuppressWarnings("resource")
    @Bean
    public TransportClient transportClient() throws UnknownHostException{

        Settings settings = Settings.builder()
                .put("cluster.name", clusterName).build();

        return new PreBuiltTransportClient(settings).addTransportAddresses(transportAddresses());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.debug("loading search templates...");
        try {
            for (Map.Entry<String, String> entry : Constants.SEARCH_TEMPLATE_MAP.entrySet()) {
                client.putInlineSearchTemplateToElasticsearch(entry.getKey(), entry.getValue());
            }
        } catch (Exception e) {
            log.error("Exception has occurred in putting search templates into ES.", e);
        }
    }

    private HttpHost[] httpHosts() {
        return stream(hosts).map(h -> new HttpHost(h, httpPort, "http")).toArray(HttpHost[]::new);
    }

    private TransportAddress[] transportAddresses() throws UnknownHostException {
        TransportAddress[] transportAddresses = stream(hosts).map(h -> {
            try {
                return new TransportAddress(getByName(h), tcpPort);
            } catch (UnknownHostException e) {
                log.error("Exception has occurred in creating ES TransportAddress. host: '{}', tcpPort: '{}'", h, tcpPort, e);
            }
            return null;
        }).filter(Objects::nonNull).toArray(TransportAddress[]::new);

        if (transportAddresses.length == 0) {
            throw new UnknownHostException();
        }
        return transportAddresses;
    }
}

Issue:

I don't know how to Mock ES or how to test ES without running an standalone ES on my machine. Please use the following class as an example and let me know how could I write a testcase (unit test not integration) for getSearchResponse method:

    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;

    import org.elasticsearch.action.search.SearchRequest;
    import org.elasticsearch.action.search.SearchResponse;
    import org.elasticsearch.client.transport.NoNodeAvailableException;
    import org.elasticsearch.client.transport.TransportClient;
    import org.elasticsearch.script.ScriptType;
    import org.elasticsearch.script.mustache.SearchTemplateRequestBuilder;
    import org.elasticsearch.search.Scroll;
    import org.elasticsearch.search.aggregations.Aggregation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.MessageSource;
    import org.springframework.stereotype.Repository;
    @Slf4j
@Repository
@NoArgsConstructor
public abstract class NewBaseElasticsearchRepository {

    @Autowired
    protected NewIndexLocator newIndexLocator;

    @Value(value = "${elasticsearch.client.timeout}")
    private Long timeout;

    @Autowired
    protected TransportClient transportClient;

    @Autowired
    protected ThresholdService thresholdService;

    @Autowired
    protected MessageSource messageSource;

    /**
     * @param script         the name of the script to be executed
     * @param templateParams a map of the parameters to be sent to the script
     * @param indexName      the index to target (an empty indexName will search all indexes)
     *
     * @return a Search Response object containing details of the request results from Elasticsearch
     *
     * @throws NoNodeAvailableException thrown when the transport client cannot connect to any ES Nodes (or Coordinators)
     * @throws Exception                thrown for all other request errors such as parsing and non-connectivity related issues
     */
    protected SearchResponse getSearchResponse(String script, Map<String, Object> templateParams, String... indexName) {
        log.debug("transport client >> index name --> {}", Arrays.toString(indexName));

        SearchResponse searchResponse;

        try {
            searchResponse = new SearchTemplateRequestBuilder(transportClient)
                .setScript(script)
                .setScriptType(ScriptType.STORED)
                .setScriptParams(templateParams)
                .setRequest(new SearchRequest(indexName))
                .execute()
                .actionGet(timeout)
                .getResponse();
        } catch (NoNodeAvailableException e) {
            log.error(ELASTIC_SEARCH_EXCEPTION_NOT_FOUND, e.getMessage());
            throw new ElasticSearchException(ELASTIC_SEARCH_EXCEPTION_NOT_FOUND);
        } catch (Exception e) {
            log.error(ELASTIC_SEARCH_EXCEPTION, e.getMessage());
            throw new ElasticSearchException(ELASTIC_SEARCH_EXCEPTION);
        }

        log.debug("searchResponse ==> {}", searchResponse);
        return searchResponse;
    }

So, I would be grateful if you could have a look on the example class and share your genuine solutions with me here about how could I mock TransportClient and get a proper response from SearchResponse object.


Note:

  • I tried to use ESTestCase from org.elasticsearch.test:framework:6.2.4 but faced jar hell issue and could't resolve it. In the meantime, I could't find any proper docs related to that or Java ES unit testing, in general.
0 Answers
Related