Интерфейсы golang для тестирования

Я пытаюсь создать макет базы данных в своем коде, затем я ввожу интерфейсы в свой код, чтобы создать макет:

Это мой код (я не знаю, правильный ли это подход)

package interfaces

type ObjectAPI interface {
    FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error)
}

И моя реализация интерфейса:

package repositories

func FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error) {

    statement, err := db.SqlStatementWithCtx(ctx,
        `SELECT * 
         FROM table
         WHERE name = ? LIMIT 1`)

    row := statement.QueryRowContext(ctx, name)

    if err != nil {
        return e.Response{}, err
    }

    statement.Close()
    return toResponse(row), nil  //this method convert row database to e.Response struct

}

Теперь мне нужно вызвать из одного метода реализацию моей FindSomethingInDatabase, тогда я получаю интерфейс типа объекта:

func CallImplementation(request *dto.XRequest, repo i.ObjectAPI) dto.XResponse{
    result := repo.FindSomethingInDatabase(request.Ctx, request.Name)
// more code
}

Но теперь я не знаю, как я могу вызвать CallImplementation`, чтобы передать объект с реализацией.

Вызвать метод, передав реализацию интерфейса

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Интерфейс описывает тип. Поскольку ваша реализация FindSomethingInDatabase — это просто функция без приемника, не существует типа, реализующего интерфейс ObjectAPI.

Вы можете передать значение типа func(ctx context.Context, name string) (e.Response, error) в CallImplementation в качестве обратного вызова и полностью избавиться от интерфейса. В качестве альтернативы оставьте интерфейс, определите тип и сделайте этот тип получателем для вашей текущей FindSomethingInDatabase реализации. Затем вы можете передать тип в CallImplementation, так как теперь он будет реализовывать интерфейс ObjectAPI. Пример последнего (который был бы моим предпочтительным вариантом для расширяемости):

type database struct {}

func (d *database) FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error) {
    // ...
}

func CallImplementation(request *dto.XRequest, repo i.ObjectAPI) dto.XResponse{
    result := repo.FindSomethingInDatabase(request.Ctx, request.Name)
// more code
}

func main() {
    db := &database{}
    _ = Callimplementation(getRequest(), db)
}

В этом случае вы, вероятно, захотите сохранить db как член database, а не использовать его как глобальную переменную (что сейчас имеет место).

Я не понимаю здесь, как я могу реализовать другой FindSomethingInDatabase2, например, возвращающий фиктивный (e.Response, ошибка) и вызывающий его после

daocc 08.02.2023 10:59

@daocc Вы бы реализовали не FindSomethingInDatabase2, а другое FindSomethingInDatabase. Но вы бы определили его с другим типом приемника. Например, у вас будет тип database (как в моем примере), а затем тип mockDatabase. У обоих есть метод FindSomethingInDatabase, и вы можете свободно пройти любой из них в CallImplementation (т. е. макет при тестировании и реальный в противном случае)

Emile Pels 08.02.2023 11:18

И странно, что если мне нужен макет, например, 3 метода, мне нужно получить в моей функции CallImplementation больше параметров, тогда мне нужно получить параметр для макета

daocc 08.02.2023 12:13

см. издевательство. он обеспечивает автоматическое создание макетов для интерфейсов и может быть хорошим справочником по передовым методам создания макетов.

обычно вы делаете это:

апи.го:

type API interface {
    Something(ctx context.Context, name string) (e.Response, error)
}

type ApiImpl struct {
  
}

func (t *ApiImpl) Something(ctx context.Context, name string) (e.Response, error) {
    // impl
}

api_test.go

// mocks can be hand coded or autogenerated using mockery
type MockImpl struct {
}

func (m *MockImpl) Something(ctx context.Context, name string) (e.Response, error) {
    // mock implementation
}

func TestSomething(t * testing.T) {
    // test code with mocks
}

Другие вопросы по теме