@0xsetup

Тестирование с реальной БД?

Решил попробовать сделать тестирование с реальной БД PostgreSQL, которая поднимается в docker. Для этого испольовал либу: https://github.com/testcontainers/testcontainers-go

Написал tHelper, который должен помогать мне с тестами:
code

type THelper struct {
	dockerContainer testcontainers.Container
	postgresPool    postgres.Client
	m               *migrate.Migrate
}

func SetupPostgresSQL(ctx context.Context) (*THelper, error) {
	resource := testcontainers.ContainerRequest{
		Image:        "postgres:latest",
		ExposedPorts: []string{"5432/tcp"},
		WaitingFor:   wait.ForListeningPort("5432/tcp"),
		Env: map[string]string{
			"POSTGRES_DB":       "web",
			"POSTGRES_PASSWORD": "postgres",
			"POSTGRES_USER":     "postgres",
		},
	}

	dockerpool, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
		ContainerRequest: resource,
		Started:          true,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to start postgres container: %w", err)
	}

	port, err := dockerpool.MappedPort(ctx, "5432")
	if err != nil {
		return nil, fmt.Errorf("failed to get mapped port: %w", err)
	}

	host, err := dockerpool.Host(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get host: %w", err)
	}

	dbURI := fmt.Sprintf("postgres://postgres:postgres@%v:%v/%s?sslmode=disable", host, port.Port(), "web")

	// TODO: подумать над путем к миграциям
	m, err := migrate.New("file://D:/dev/webshop/migrations", dbURI)
	if err != nil {
		return nil, fmt.Errorf("failed to create migrate instance: %w", err)
	}

	postgresPool, err := postgres.NewPool(ctx, dbURI)
	if err != nil {
		return nil, fmt.Errorf("failed to create postgres pool: %w", err)
	}

	return &THelper{
		dockerContainer: dockerpool,
		postgresPool:    postgresPool,
		cfg:             cfg,
		m:               m,
	}, nil
}

func (p *THelper) GetPostgresPool() postgres.Client {
	return p.postgresPool
}

func (p *THelper) GetDockerContainer() testcontainers.Container {
	return p.dockerContainer
}

func (p *THelper) RemoveContainer() error {
	return p.dockerContainer.Terminate(context.Background())
}


func (p *THelper) MigrateUp() error {
	return p.m.Up()
}

func (p *THelper) MigrateDown() error {
	return p.m.Down()
}


Данный код прекрасно работает в слое "repository", тесты проходят прекрасно и все гуд работает. Но я его тащил ради теста слоя "service", и вот тут как раз возникает проблема с зависимостями.
Код userService
code
type WebService struct {
	uRepo             *userRepo.UsersRepository
	rRepo             *roleRepo.RolesRepository
	rSessionsRepo     *refreshSessionsRepo.RefreshSessionsRepository
	activateCodesRepo *activationCodesRepo.ActivationCodesRepository
	ramblerSMTP       *sender.RamblerSMTPServer
	jwt               *jwtAuth.JWT
}

func NewWebService(
	uRepo *userRepo.UsersRepository,
	rRepo *roleRepo.RolesRepository,
	rSessionsRepo *refreshSessionsRepo.RefreshSessionsRepository,
	activateCodesRepo *activationCodesRepo.ActivationCodesRepository,
	ramblerSMTP *sender.RamblerSMTPServer,
	jwt *jwtAuth.JWT,
) *WebService {
	return &WebService{
		uRepo:             uRepo,
		rRepo:             rRepo,
		rSessionsRepo:     rSessionsRepo,
		activateCodesRepo: activateCodesRepo,
		ramblerSMTP:       ramblerSMTP,
		jwt:               jwt,
	}
}


Вот тут как раз-таки происходит проблема с зависимостями, потому что многим нужны данные из моего конфига. Вроде решается это просто, добавить в tHelper конфиг и функцию GetConfig, которая будет возвращать указатель на конфиг.

Я как раз так и сделал:
code
type THelper struct {
	dockerContainer testcontainers.Container
	cfg             *config.App
	postgresPool    postgres.Client
	m               *migrate.Migrate
}

func SetupPostgresSQL(ctx context.Context) (*THelper, error) {
	cfg := config.LoadYML()

	resource := testcontainers.ContainerRequest{
		Image:        "postgres:latest",
		ExposedPorts: []string{"5432/tcp"},
		WaitingFor:   wait.ForListeningPort("5432/tcp"),
		Env: map[string]string{
			"POSTGRES_DB":       "web",
			"POSTGRES_PASSWORD": "postgres",
			"POSTGRES_USER":     "postgres",
		},
	}

	dockerpool, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
		ContainerRequest: resource,
		Started:          true,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to start postgres container: %w", err)
	}

	port, err := dockerpool.MappedPort(ctx, "5432")
	if err != nil {
		return nil, fmt.Errorf("failed to get mapped port: %w", err)
	}

	host, err := dockerpool.Host(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get host: %w", err)
	}

	dbURI := fmt.Sprintf("postgres://postgres:postgres@%v:%v/%s?sslmode=disable", host, port.Port(), "web")

	// TODO: подумать над путем к миграциям
	m, err := migrate.New("file://D:/dev/webshop/migrations", dbURI)
	if err != nil {
		return nil, fmt.Errorf("failed to create migrate instance: %w", err)
	}

	postgresPool, err := postgres.NewPool(ctx, dbURI)
	if err != nil {
		return nil, fmt.Errorf("failed to create postgres pool: %w", err)
	}

	return &THelper{
		dockerContainer: dockerpool,
		postgresPool:    postgresPool,
		cfg:             cfg,
		m:               m,
	}, nil
}

func (p *THelper) GetPostgresPool() postgres.Client {
	return p.postgresPool
}

func (p *THelper) GetDockerContainer() testcontainers.Container {
	return p.dockerContainer
}

func (p *THelper) RemoveContainer() error {
	return p.dockerContainer.Terminate(context.Background())
}

func (p *THelper) GetConfig() *config.App {
	return p.cfg
}

func (p *THelper) MigrateUp() error {
	return p.m.Up()
}

func (p *THelper) MigrateDown() error {
	return p.m.Down()
}


и вот тут происходит ошибка:
open ./config/config.yml: The system cannot find the path specified
т.е он банально не находит мой конфиг

функция LoadYML
type App struct {
	AppVersion  string `yaml:"app_version"`
	Domain      string `yaml:"domain"`
	Server      `yaml:"server"`
	JWT         `yaml:"jwt"`
	RamblerSMTP `yaml:"rambler_smtp"`
	Logger      `yaml:"logger"`
	PostgresSQL `yaml:"postgres"`
}

type Server struct {
	Port         string        `yaml:"port"`
	CookieName   string        `yaml:"cookie_name"`
	Mode         string        `yaml:"mode"`
	ReadTimeout  time.Duration `yaml:"read_timeout"`
	WriteTimeout time.Duration `yaml:"write_timeout"`
	SSL          bool          `yaml:"ssl"`
	Debug        bool          `yaml:"debug"`
}

type JWT struct {
	Secret     string        `yaml:"secret"`
	AccessTTL  time.Duration `yaml:"accessTTL"`
	RefreshTTL time.Duration `yaml:"refreshTTL"`
}

type RamblerSMTP struct {
	Host     string `yaml:"host"`
	Port     string `yaml:"port"`
	Email    string `yaml:"email"`
	Password string `yaml:"password"`
}

type Logger struct {
	DisableCaller     bool   `yaml:"disable_caller"`
	DisableStacktrace bool   `yaml:"disable_stacktrace"`
	Level             string `yaml:"level"`
}

type PostgresSQL struct {
	Host     string `yaml:"host"`
	Port     string `yaml:"port"`
	Username string `yaml:"username"`
	Password string `yaml:"password"`
	DBName   string `yaml:"db_name"`
	SSLMode  string `yaml:"ssl_mode"`
}

var (
	//configPath string
	instance *App
	once     sync.Once
)

// LoadYML returns a completed instance of the App.
func LoadYML() *App {
	once.Do(func() {
		instance = &App{}

		if err := cleanenv.ReadConfig("./config/config.yml", instance); err != nil {
			log.Fatal(err)
		}
	})

	return instance
}


Как можно решить данную проблему с зависимостями, которым нужны данные из конфига, но он не может его найти?
И нужно ли так тестировать мой код, в чем будет преимущество моков, сравнивая с этим тестированием?
  • Вопрос задан
  • 127 просмотров
Пригласить эксперта
Ответы на вопрос 1
@calculator212
В общем у тебя жестко забит путь к конфигу, если это тесты, то вместо конифиг файла, ты можешь просто переменную с ним добавить и читать строку, будет работать абсолютно всегда. Если не хочешь так делать, что можешь разобраться с относительными, абсолютными путями в го и рабочими директориями во время тестирования.
тут можно начать
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
24 нояб. 2024, в 21:55
200000 руб./за проект
24 нояб. 2024, в 21:41
1000 руб./за проект
24 нояб. 2024, в 21:19
35000 руб./за проект