0

I have an application that defines a type Client struct {} which talks to various other clients in my code that talk to services like github, elasticsearch etc.

Now I have the following ES code in one of my packages

type SinkService interface {
    Write(context, index, mapping, doc)
}

type ESSink struct {
   client *elastic.Client
}

func NewESSink() *ESSink {}

 // checks if the index exists and writes the doc
func (s *ESSink) Write(context, index, mapping, doc) {}

I use this method in my main client that runs the whole application like this c.es.Write(...). Now if I want to write client_test.go I can simply make a mockESSink and use it with some stub code but that won't cover the lines written in my ES code.

How do I unit test my ES code? My ESSink uses an elastic.Client. How do I mock that?

I would like to embed some mock ES client that gives me stub responses and I will be able to test my ESSink.Write method that way.

1
  • Same way you mock anything, you'd have to replace the elastic.Client field with an interface that defines the methods you use, and then create a mock that satisfies the interface for use in your tests. Commented Mar 10, 2020 at 17:06

1 Answer 1

2

Based on your question, I assume you're using github.com/olivere/elastic, and you want to be able to test by using stub http responses. When I first read this question, I also have never written Go test code that use ES client. So, in addition to answering this question, I'm also sharing how I find out the answer from the godocs.

First, we can see that elastic.NewClient accepts client option functions. So I checked what kind of client option functions the library provides. Turns out the library provides elastic.SetHttpClient that accepts elastic.Doer. The Doer is an interface that http.Client can implement. From here, the answer becomes clear.

So, you have to:

  1. Change your func NewESSink() to accept http Client or elastic Client.
  2. Write stub http Client (implements elastic.Doer).

ESSink

type ESSink struct {
    client *elastic.Client
}

func NewESSink(client *elastic.Client) *ESSink {
    return &ESSink{client: client}
}

Stub HttpClient

package stubs

import "net/http"

type HTTPClient struct {
    Response *http.Response
    Error    error
}

func (c *HTTPClient) Do(*http.Request) (*http.Response, error) {
    return c.Response, c.Error
}

Your testing code

func TestWrite(t *testing.T) {
    // set the body and error according to your test case
    stubHttpClient := stubs.HTTPClient{ 
        Response: &http.Response{Body: ...},
        Error: ...,
    }

    elasticClient := elastic.NewClient(elastic.SetHttpClient(stubHttpClient))
    esSink := NewESSink(elasticClient)
    esSink.Write(...)
}

In your production code, you can use http.Client{} when setting ES http client.

Sign up to request clarification or add additional context in comments.

1 Comment

I never even bothered to take a look at the options, was thinking about dropping the idea of testing this altogether, thanks so much @erick-wijaya

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.