Я пытаюсь создать макет базы данных в своем коде, затем я ввожу интерфейсы в свой код, чтобы создать макет:
Это мой код (я не знаю, правильный ли это подход)
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
`, чтобы передать объект с реализацией.
Вызвать метод, передав реализацию интерфейса
Интерфейс описывает тип. Поскольку ваша реализация 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
, а не использовать его как глобальную переменную (что сейчас имеет место).
@daocc Вы бы реализовали не FindSomethingInDatabase2
, а другое FindSomethingInDatabase
. Но вы бы определили его с другим типом приемника. Например, у вас будет тип database
(как в моем примере), а затем тип mockDatabase
. У обоих есть метод FindSomethingInDatabase
, и вы можете свободно пройти любой из них в CallImplementation
(т. е. макет при тестировании и реальный в противном случае)
И странно, что если мне нужен макет, например, 3 метода, мне нужно получить в моей функции CallImplementation больше параметров, тогда мне нужно получить параметр для макета
см. издевательство. он обеспечивает автоматическое создание макетов для интерфейсов и может быть хорошим справочником по передовым методам создания макетов.
обычно вы делаете это:
апи.го:
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
}
Я не понимаю здесь, как я могу реализовать другой FindSomethingInDatabase2, например, возвращающий фиктивный (e.Response, ошибка) и вызывающий его после