Как смоделировать метод, который зависит от других методов - PullRequest
0 голосов
/ 17 марта 2019

Я тестирую промежуточное ПО эластичного поиска.Он реализует службу переиндексации, подобную этой:

type reindexService interface {
    reindex(ctx context.Context, index string, mappings, settings map[string]interface{}, includes, excludes, types []string) error
    mappingsOf(ctx context.Context, index string) (map[string]interface{}, error)
    settingsOf(ctx context.Context, index string) (map[string]interface{}, error)
    aliasesOf(ctx context.Context, index string) ([]string, error)
    createIndex(ctx context.Context, name string, body map[string]interface{}) error
    deleteIndex(ctx context.Context, name string) error
    setAlias(ctx context.Context, index string, aliases ...string) error
    getIndicesByAlias(ctx context.Context, alias string) ([]string, error)
}

Я могу легко протестировать все методы, используя этот шаблон.Создание простого эластичного поискового клиента с использованием URL-адреса сервера httptest и отправка запросов на этот сервер

var createIndexTests = []struct {
    setup *ServerSetup
    index string
    err   string
}{
    {
        &ServerSetup{
            Method:   "PUT",
            Path:     "/test",
            Body:     `null`,
            Response: `{"acknowledged": true, "shards_acknowledged": true, "index": "test"}`,
        },
        "test",
        "",
    },
   // More test cases here
}

func TestCreateIndex(t *testing.T) {
    for _, tt := range createIndexTests {
        t.Run("Should successfully create index with a valid setup", func(t *testing.T) {
            ctx := context.Background()
            ts := buildTestServer(t, tt.setup)
            defer ts.Close()
            es, _ := newTestClient(ts.URL)
            err := es.createIndex(ctx, tt.index, nil)
            if !compareErrs(tt.err, err) {
                t.Fatalf("Index creation should have failed with error: %v got: %v instead\n", tt.err, err)
            }
        })
    }
}

Но в случае метода reindex этот подход создает проблему, поскольку reindex выполняет вызовы всех других методов внутри своеготело.Reindex выглядит примерно так:

func (es *elasticsearch) reindex(ctx context.Context, indexName string, mappings, settings map[string]interface{}, includes, excludes, types []string) error {
    var err error

    // Some preflight checks

    // If mappings are not passed, we fetch the mappings of the old index.
    if mappings == nil {
        mappings, err = es.mappingsOf(ctx, indexName)
        // handle err
    }

    // If settings are not passed, we fetch the settings of the old index.
    if settings == nil {
        settings, err = es.settingsOf(ctx, indexName)
        // handle err
    }

    // Setup the destination index prior to running the _reindex action.
    body := make(map[string]interface{})
    body["mappings"] = mappings
    body["settings"] = settings

    newIndexName, err := reindexedName(indexName)
    // handle err

    err = es.createIndex(ctx, newIndexName, body)
    // handle err

    // Some additional operations

    // Reindex action.
    _, err = es.client.Reindex().
        Body(reindexBody).
        Do(ctx)
    // handle err

    // Fetch all the aliases of old index
    aliases, err := es.aliasesOf(ctx, indexName)
    // handle err
    aliases = append(aliases, indexName)

    // Delete old index
    err = es.deleteIndex(ctx, indexName)
    // handle err

    // Set aliases of old index to the new index.
    err = es.setAlias(ctx, newIndexName, aliases...)
    // handle err

    return nil
}

Я думал о том, чтобы смоделировать функции, используемые Reindex, и каким-то образом внедрить их в структуру es, чтобы mock вызывались внутри reindex, но исходная функция Reindexдолжен вызываться, поскольку я хочу, чтобы исходная функция была покрыта моими тестами.

Я прочитал много статей о насмешках и DI, но все еще не могу обойти эту проблему.Например, если у меня есть такая структура mock es, которая реализует тот же интерфейс, и я хочу использовать ее при тестировании метода переиндексации

type mockES struct {
    url    string
    client *elastic.Client
}

func newMockElasticsearch(url string) (*mockES, error) {
    client, err := elastic.NewSimpleClient(elastic.SetURL(url))
    if err != nil {
        return nil, fmt.Errorf("error while initializing elastic client: %v", err)
    }
    es := &mockES{
        url:    url,
        client: client,
    }
    return es, nil
}

func (m *mockES) mappingsOf(ctx context.Context, index string) (map[string]interface{}, error) {
    return m.mockMappingsOf(ctx, index), nil
}

func (m *mockES) mockMappingsOf(ctx context.Context, index string) map[string]interface{} {
    // return some pre-defined response
}

Что я не могу понять, так это то, что если я вызываю переиндексацию, определеннуюна mockES только тогда я смогу использовать эти фиктивные методы, но я хотел бы вызвать оригинальный метод переиндексации, определенный в структуреasticsearch, и вызовы других методов были бы сделаны для фиктивных.

возможно ли это достичь, учитывая состояние реализации кодовой базы в настоящее время, или мне придется изменить дизайн моей реализации, чтобы иметь возможность писать тесты для нее?

...