Skip to content
Oliver Eilhard edited this page Jun 19, 2020 · 1 revision

There are various ways to test your code against Elasticsearch. The fastest way is to not run an integration test against a real Elasticsearch cluster, but mock it. You can do so by providing a service and mock its return values in testing.

Mocking

Mocking works by specifying an interface that has two implementations: One that uses a real ES cluster, and one that uses callbacks used in testing. Here's an example to get you started:

package search

type Searcher interface {
	Search(context.Context, SearchRequest) (*SearchResponse, error)
}

// ESSearcher will be used with a real ES cluster.
type ESSearcher struct {
	client *elastic.Client
}

func (s *ESSearcher) Search(ctx context.Context, req SearchRequest) (*SearchResponse, error) {
	// Use s.client to run against real ES cluster and perform a search
}

// MockedSearcher can be used in testing.
type MockedSearcher struct {
	OnSearch func(context.Context, SearchRequest) (*SearchResponse, error)
}

func (s *ESSearcher) Search(ctx context.Context, req SearchRequest) (*SearchResponse, error) {
	return s.OnSearch(ctx, req)
}

Testing

The alternative is to run a real Elasticsearch cluster while testing. One particular nice way might be to start the ES cluster during testing with something like github.com/ory/dockertest. Here's an example to get you started:

package search

import (
	"context"
	"fmt"
	"log"
	"os"
	"testing"

	"github.com/olivere/elastic/v7"
	"github.com/ory/dockertest/v3"
	"github.com/ory/dockertest/v3/docker"
)

// client will be initialize in TestMain
var client *elastic.Client

func TestMain(m *testing.M) {
	pool, err := dockertest.NewPool("")
	if err != nil {
		log.Fatalf("unable to create new pool: %v", err)
	}

	options := &dockertest.RunOptions{
		Repository: "docker.elastic.co/elasticsearch/elasticsearch-oss",
		Tag:        "7.8.0",
		PortBindings: map[docker.Port][]docker.PortBinding{
			"9200": {{HostPort: "9200"}},
		},
		Env: []string{
			"cluster.name=elasticsearch",
			"bootstrap.memory_lock=true",
			"discovery.type=single-node",
			"network.publish_host=127.0.0.1",
			"logger.org.elasticsearch=warn",
			"ES_JAVA_OPTS=-Xms1g -Xmx1g",
		},
	}
	resource, err := pool.RunWithOptions(options)
	if err != nil {
		log.Fatalf("unable to ES: %v", err)
	}
	endpoint := fmt.Sprintf("http://127.0.0.1:%s", resource.GetPort("9200/tcp"))

	if err := pool.Retry(func() error {
		var err error
		client, err = elastic.NewClient(
			elastic.SetURL(endpoint),
			elastic.SetSniff(false),
			elastic.SetHealthcheck(false),
		)
		if err != nil {
			return err
		}
		_, _, err = client.Ping(endpoint).Do(context.Background())
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		log.Fatalf("unable to connect to ES: %v", err)
	}

	code := m.Run()

	if err := pool.Purge(resource); err != nil {
		log.Fatalf("unable to stop ES: %v", err)
	}

	os.Exit(code)
}

func TestAgainstRealCluster(t *testing.T) {
	// You can use "client" variable here

	// Example code:
	exists, err := client.IndexExists("cities-test").Do(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if !exists {
		t.Fatal("expected to find ES index")
	}
}