2015-02-11 3 views
2

Я создал быстрый и легкий API в Go, который запрашивает ElasticSearch. Теперь, когда я знаю, что это можно сделать, я хочу сделать это правильно, добавив тесты. Я отвлек часть своего кода, чтобы его можно было тестировать на единицу, но у меня были некоторые проблемы, издевавшиеся над эластичной библиотекой, и поэтому я полагал, что было бы лучше, если бы я попробовал простой случай, чтобы издеваться над этим.Golang Mocking with Elastic

import (
    "encoding/json" 
    "github.com/olivere/elastic" 
    "net/http" 
) 
... 
func CheckBucketExists(name string, client *elastic.Client) bool { 
    exists, err := client.IndexExists(name).Do() 
    if err != nil { 
     panic(err) 
    } 
    return exists 
} 

А теперь тест ...

import (
     "fmt" 
     "github.com/stretchr/testify/assert" 
     "github.com/stretchr/testify/mock" 
     "testing" 
) 

type MockClient struct { 
     mock.Mock 
    } 

    func (m *MockClient) IndexExists(name string) (bool, error) { 
     args := m.Mock.Called() 
     fmt.Println("This is a thing") 
     return args.Bool(0), args.Error(1) 
    } 

    func TestMockBucketExists(t *testing.T) { 
     m := MockClient{} 
     m.On("IndexExists", "thisuri").Return(true) 

>> r := CheckBucketExists("thisuri", m) 
     assert := assert.New(t) 
     assert.True(r, true) 
    } 

К которому я уступил со следующей ошибкой: cannot use m (type MockClient) as type *elastic.Client in argument to CheckBucketExists.

Я предполагаю, что это что-то фундаментальное с моим использованием типа эластичный .client, но я все еще слишком много нуба.

ответ

1

Линия

func CheckBucketExists(name string, client *elastic.Client) bool { 

утверждает, что CheckBucketExists ожидает *elastic.Client.

линии:

m := MockClient{} 
m.On("IndexExists", "thisuri").Return(true) 
r := CheckBucketExists("thisuri", m) 

пасс MockClient функции CheckBucketExists.

Это вызывает конфликт типа.

Возможно, вам нужно импортировать github.com/olivere/elastic в свой тестовый файл и сделать:

m := &elastic.Client{} 

вместо

m := MockClient{} 

Но я не 100% уверен, что вы пытаетесь сделать.

0

Это старый вопрос, но также не смог найти решение. К сожалению, эта библиотека реализована с использованием-структуры, что делает насмешливое не тривиальное на всех, поэтому варианты, которые я нашел являются:

(1) Оберните все elastic.SearchResult метод на интерфейсе самостоятельно и «прокси» вызов, так что вы в конечном итоге с чем-то вроде:

type ObjectsearchESClient interface { 
    // ... all methods... 
    Do(context.Context) (*elastic.SearchResult, error) 
} 


// NewObjectsearchESClient returns a new implementation of ObjectsearchESClient 
func NewObjectsearchESClient(cluster *config.ESCluster) (ObjectsearchESClient, error) { 
    esClient, err := newESClient(cluster) 
    if err != nil { 
     return nil, err 
    } 

    newClient := objectsearchESClient{ 
     Client: esClient, 
    } 
    return &newClient, nil 
} 
// ... all methods... 
func (oc *objectsearchESClient) Do(ctx context.Context) (*elastic.SearchResult, error) { 
    return oc.searchService.Do(ctx) 
} 

а потом издеваться этот интерфейс и ответы, как вы бы с другими модулями приложения.

(2) Другой вариант, как указывалось в this blog post что фиктивный ответ от остальных вызовов с использованием httptest.Server

для этого, я издевался обработчик, которые состоят из насмешливого ответа от «HTTP вызова»

func mockHandler() http.HandlerFunc{ 
    return func(w http.ResponseWriter, r *http.Request) { 
     resp := `{ 
      "took": 73, 
      "timed_out": false, 
      ... json ... 
      "hits": [... ] 
      ...json ... , 
      "aggregations": { ... } 
     }` 

     w.Write([]byte(resp)) 
    } 
} 

Затем создается фиктивный elastic.Client-структуру

func mockClient(url string) (*elastic.Client, error) { 
    client, err := elastic.NewSimpleClient(elastic.SetURL(url)) 
    if err != nil { 
     return nil, err 
    } 
    return client, nil 
} 

В этом случае, у меня есть библиотека, которая строит мою резину.SearchService и возвращает его, поэтому я использую HTTP, как:

... 
    ts := httptest.NewServer(mockHandler()) 
    defer ts.Close() 
    esClient, err := mockClient(ts.URL) 
    ss := elastic.NewSearchService(esClient) 
    mockLibESClient := es_mock.NewMockSearcherClient(mockCtrl) 
    mockLibESClient.EXPECT().GetEmployeeSearchServices(ctx).Return(ss, nil) 

где mockLibESClient является библиотекой я уже говорил, и мы окурок метода mockLibESClient.GetEmployeeSearchServices делают его вернуть SearchService с этим будет возвращать ожидаемый полезный груз.

Примечание: для создания фиктивного mockLibESClient я использовал https://github.com/golang/mock

Я нашел, что это будет свернуто, но «упаковка» elastic.Client был в моей точке зрения более работы.

Вопрос: Я попытался издеваться над этим, используя https://github.com/vburenin/ifacemaker, чтобы создать интерфейс, а затем издеваться над этим интерфейсом с https://github.com/golang/mock и использовать его, но я продолжал получать ошибки совместимости при попытке вернуть интерфейс вместо структуры, я «Я не ожидаю этого, поэтому, вероятно, мне нужно было понять, что приведение немного лучше, чтобы это можно было решить. Поэтому, если кто-то из вас знает, как это сделать, пожалуйста, дайте мне знать.

+0

Первоначально это был мой вопрос, и я многого не делал с тех пор, как я опубликовал эти годы назад. [CodeReviewComments # Интерфейсы] (https://github.com/golang/go/wiki/CodeReviewComments#interfaces) предполагает, что внешняя библиотека должна реализовать структуру, и пользователь должен определить интерфейс в этой структуре, передав ее при использовании dep инъекции. Что, указывает на первое решение, которое вы упомянули в своем посте! – user2402831