Всем привет! Меня зовут Андрей, я Go-разработчик. Сегодня хочу поделиться библиотекой, которая родилась из внутренней боли и желания оптимизировать рабочий процесс.
Проблема: «Ну сколько можно ждать?»
Классический сценарий подготовки базы для интеграционного теста выглядит так:
func TestMyService(t *testing.T) {
// 1. Создать новую БД (CREATE DATABASE)
// 2. Применить все миграции (N запросов CREATE TABLE, INDEX, FK...)
// 3. Запустить сам тест
// 4. Удалить БД (DROP DATABASE)
// ... и так для КАЖДОГО теста.
}
Шаги 1 и 2 повторяются каждый раз, съедая кучу времени. Чем сложнее ваша схема (таблицы, индексы, внешние ключи), тем дольше длится этот процесс.
Решение: Шаблоны (Templates) PostgreSQL
В PostgreSQL есть мощная, но не всегда очевидная фича — шаблонные базы данных (Template Databases). Вы можете создать одну «шаблонную» базу, применить все миграции единожды и сделать ее шаблоном. Все последующие базы создаются командой:
CREATE DATABASE my_fast_test_db TEMPLATE my_template_db;
Эта операция копирует данные на уровне файловой системы и занимает мгновение, независимо от сложности схемы.
Моя библиотека pgdbtemplate
автоматизирует всю эту магию, предоставляя простой и удобный API для ваших тестов.
Начинаем работать за 5 минут
Установка стандартная:
go get github.com/andrei-polukhin/pgdbtemplate
А вот так это выглядит в коде ваших тестов:
package mytest
import (
"context"
"os"
"testing"
"github.com/andrei-polukhin/pgdbtemplate"
)
var tm *pgdbtemplate.TemplateManager
func TestMain(m *testing.M) {
ctx := context.Background()
// Функция для формирования строки подключения к любой БД
connStrFunc := func(dbName string) string {
return "postgres://user:pass@localhost/" + dbName
}
// Создаем провайдер для подключения через стандартный database/sql + pq
provider := pgdbtemplate.NewStandardConnectionProvider(connStrFunc)
// Говорим, где лежат наши миграции (SQL-файлы)
migrator := pgdbtemplate.NewFileMigrationRunner(
[]string{"./migrations"},
pgdbtemplate.AlphabeticalMigrationFilesSorting, // сортировка по алфавиту
)
// Создаем менеджер
var err error
tm, err = pgdbtemplate.NewTemplateManager(pgdbtemplate.Config{
ConnectionProvider: provider,
MigrationRunner: migrator,
})
if err != nil {
panic(err)
}
// ! ВАЖНО: Инициализируем шаблон (делаем всё ОДИН раз)
if err := tm.Initialize(ctx); err != nil {
panic(err)
}
// Запускаем тесты
exitCode := m.Run()
// Подчищаем за собой (удаляем все тестовые БД и шаблон)
tm.Cleanup(ctx)
os.Exit(exitCode)
}
func TestMySuperService(t *testing.T) {
ctx := context.Background()
// Каждый тест получает свою собственную, чистую БД!
testDB, testDBName, err := tm.CreateTestDatabase(ctx)
if err != nil {
t.Fatalf("Не удалось создать БД: %v", err)
}
defer testDB.Close() // Закрываем подключение
defer tm.DropTestDatabase(ctx, testDBName) // Удаляем БД
// А тут уже ваш тест...
repo := NewUserRepository(testDB)
user, err := repo.CreateUser("ivan@example.com")
if err != nil {
t.Errorf("Ошибка создания пользователя: %v", err)
}
// ... ваши assertions
}
Цифры говорят сами за себя
Я провел детальные бенчмарки, сравнивая традиционный подход и подход с шаблонами. Результаты впечатляют:
? Сравнение скорости (меньше — лучше)
Сложность схемы |
Классический подход |
Через шаблоны |
Ускорение |
---|---|---|---|
1 таблица |
28.9 мс |
28.2 мс |
1.03x |
3 таблицы |
39.5 мс |
27.6 мс |
1.43x |
5 таблиц (+индексы) |
43.1 мс |
28.8 мс |
1.50x |
? Массовое создание баз
Количество баз |
Классический подход |
Через шаблоны |
Экономия времени |
---|---|---|---|
20 баз |
906.8 мс |
613.8 мс |
32% |
50 баз |
2.29 с |
1.53 с |
33% |
200 баз |
9.21 с |
5.84 с |
37% |
500 баз |
22.31 с |
14.82 с |
34% |
Главный вывод: скорость подхода с шаблонами не зависит от сложности схемы. Пока классический метод будет всё больше замедляться с ростом числа таблиц и индексов, метод с шаблонами остается стабильно быстрым.
Что под капотом?
Инициализация: Создается база-шаблон, на нее один раз накатываются все миграции.
Тестирование: Для каждого теста создается новая база через
CREATE DATABASE ... TEMPLATE
— это быстрое копирование на уровне файловой системы PostgreSQL.Очистка: После всех тестов удаляются все созданные тестовые базы и сам шаблон.
Для кого этот инструмент?
У Вас больше 10 тестов, связанных с базой данных.
Ваша схема данных сложнее 2-3 таблиц.
Вы часто запускаете тесты во время разработки.
Ваш CI-пайплайн включает этап с интеграционными тестами БД.
Вы цените свое время и не хотите ждать лишние 10 секунд при каждом запуске.
Полезные ссылки
GitHub репозиторий: github.com/andrei-polukhin/pgdbtemplate
Документация (ENG): pkg.go.dev
Буду рад вашим звёздочкам на GitHub, пул-реквестам и issue! Что думаете о таком подходе? Сталкивались ли с подобной проблемой и как решали её раньше?
Большое спасибо за прочтение поста!
Комментарии (0)
xcono
17.09.2025 08:00Также можно взглянуть на https://github.com/peterldowns/pgtestdb - проекты очень похожи.
anaxita
Я что то так и не понял чем это решение отличается от вызова create database from template и drop database в конце?