Сообщество Go любит интерфейсы, и они правда мощные. Но в какой-то момент «пиши тестируемый код» превратилось в «оборачивай в интерфейс вообще всё»:
type UserRepository interface {
GetByID(ctx context.Context, id string) (*User, error)
Create(ctx context.Context, u *User) error
Update(ctx context.Context, u *User) error
Delete(ctx context.Context, id string) error
}
// и мок на 120 строк, который расходится с реальной
// реализацией в тот же миг, когда кто-то добавил методМок становится грузом в поддержке. Он не говорит, корректен ли ваш SQL запрос. Он не ловит разницу в поведении
pgx между pgx.ErrNoRows и реальной ошибкой скана. И он даёт ложную уверенность, тесты зелёные, а боевые запросы кривые.➡️ Что делать вместо
Тестировать код базы против настоящей базы.
testcontainers-go поднимает реальный Postgres прямо в CI. Код, сгенерированный sqlc, типизирован и корректен по построению. Интерфейсы остаются там, где они нужны по делу, то есть на границах сервисов, где вы реально подменяете реализацию:
func TestCreateMember(t *testing.T) {
ctx := context.Background()
db := testutil.NewPostgresContainer(t) // реальный postgres, реальная схема
q := db_gen.New(db)
member, err := q.CreateMember(ctx, db_gen.CreateMemberParams{
FullName: "Witty",
Phone: "+254700000000",
})
require.NoError(t, err)
require.Equal(t, "Witty", member.FullName)
}Интерфейс под каждый репозиторий часто тестирует сам себя, а не вашу логику работы с данными. Базу проверяйте на реальной базе, а интерфейсы держите на стыках сервисов, где подмена реализации нужна по делу.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction