Разработка на основе тестов для проверки запросов к базе данных с использованием методов

Я хочу создать приложение, управляемое базой данных, используя Golang. Я пытаюсь сделать это TDD. Когда я пытаюсь проверить методы, которые делают запросы Sql, что все пакеты доступны?

  • Я не хочу подключаться к базе данных по умолчанию, которую я использую для разработки. Я могу написать код, чтобы взять еще одну тестовую базу данных при запуске теста, но есть ли какая-либо библиотека, которая уже делает это.

  • Есть ли какая-либо библиотека, которая делает тесты db без подключения к базе данных?

Каков стандартный способ тестирования базы данных с помощью golang?

Ответ 1

У меня был аналогичный вопрос недавно, когда рефакторинг некоторых из моих собственных тестов, и есть несколько способов, которыми вы можете это сделать:

a) Предоставьте экспортированный тип и функцию Open или Connect которая возвращает его, например:

type DB struct {
    db *sql.DB
}

// Using http://jmoiron.github.io/sqlx/ for this example, but
// it has the same interface as database/sql
func Open(opts *Options) (*DB, error) {
    db, err := sqlx.Connect(opts.Driver, fmt.Sprintf("host=%s user=%s dbname=%s sslmode=%s", opts.Host, opts.User, opts.Name, opts.SSL))
    if err != nil {
        return nil, err
    }

    return &DB{db}, nil
}

... и затем каждый из ваших тестов, напишите функции настройки и разрыва, которые возвращают экземпляр *DB который вы определяете свои функции базы данных (как методы - например, func (db *DB) GetUser(user *User) (bool, error)):

// Setup the test environment.
func setup() (*DB, error) {
    err := withTestDB()
    if err != nil {
        return nil, err
    }

    // testOptions is a global in this case, but you could easily
    // create one per-test
    db, err := Open(testOptions)
    if err != nil {
        return nil, err
    }

    // Loads our test schema
    db.MustLoad()
    return db, nil
}

// Create our test database.
func withTestDB() error {
    db, err := open()
    if err != nil {
        return err
    }
    defer db.Close()

    _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s;", testOptions.Name))
    if err != nil {
        return err
    }

    return nil
}

Обратите внимание, что это несколько "интеграционное" тестирование, но я предпочитаю тестировать "настоящую" базу данных, так как насмехающийся интерфейс не поможет вам уладить проблемы с синтаксисом запросов/запросов.

b) Альтернатива, хотя и менее расширяемая на стороне приложения, состоит в том, чтобы иметь глобальную переменную db *sql.DB которую вы инициализируете в init() в своих тестах, поскольку тесты не имеют гарантированного порядка, вам нужно будет использовать init() - и затем выполните тесты оттуда. т.е.

var db *sql.DB

func init() {
    var err error
    // Note the = and *not* the assignment - we don't want to shadow our global
    db, err = sqlx.Connect(...)
    if err != nil {
        ...
    }

    err := db.loadTestSchema
    // etc.
}

func TestGetUser(t *testing.T) {
   user := User{}
   exists, err := db.GetUser(user)
   ...
}

Вы можете найти некоторые практические примеры в drone.io GitHub repo, и я также рекомендовал бы эту статью о структурировании приложений Go (особенно данных БД).

Ответ 2

Я использую глобальную переменную для хранения источника данных (или строки подключения) текущей базы данных и для разных значений в тестовой функции. Поскольку мне нужна только одна база данных, поэтому я выбираю самый простой способ.