Golang Mocking SQL Connection in Unit Tests with sqlmock – Практическое руководство

Модульное тестирование функций, связанных с базами данных, может быть сложным, поскольку вы не хотите полагаться на реальную базу данных для выполнения тестов. Вместо этого вы можете использовать библиотеку-макет, например sqlmock, для имитации подключения к базе данных и тестирования кода в изоляции. В этой статье мы расскажем вам, как использовать sqlmock для создания имитации подключения к базе данных и перехвата SQL-запросов на практическом примере.
Начало работы
Сначала необходимо установить пакет sqlmock. Выполните следующую команду:
go get -u github.com/DATA-DOG/go-sqlmock
Теперь давайте рассмотрим приведенный ниже код примера, который тестирует функцию GetTableDetails структуры OracleConsumer.
Пример кода
// TestOracleConsumer_getTableDetails tests the getTableDetails function.
func TestOracleConsumer_getTableDetails(t *testing.T) {
testCases := []struct {
name string
schemaName string
tblName string
expectedRowsData [][]driver.Value
expectedResult []db_connection.TableColumn
}{
{
name: "Get table details",
schemaName: "SCHEMA_NAME",
tblName: "TABLE_NAME",
expectedRowsData: [][]driver.Value{
{"column1", "VARCHAR2"},
{"column2", "NUMBER"},
},
expectedResult: []db_connection.TableColumn{
{ColumnName: "column1", DataType: "VARCHAR2"},
{ColumnName: "column2", DataType: "NUMBER"},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("An error occurred while creating mock: %s", err)
}
defer db.Close()
consumer := db_connection.New(db)
// Define the expected rows data
expectedRows := sqlmock.NewRows([]string{"COLUMN_NAME", "DATA_TYPE"})
for _, row := range tc.expectedRowsData {
expectedRows.AddRow(row...)
}
// Prepare the query to be intercepted and return the expected rows
mock.ExpectQuery(db_connection.QueryGetTableDetails).WithArgs(tc.tblName, tc.schemaName).WillReturnRows(expectedRows)
// Call the function
tableColumns, err := consumer.GetTableDetails(tc.schemaName, tc.tblName)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
// Check the result
if len(tableColumns) != len(tc.expectedResult) {
t.Fatalf("Expected %d table columns, but got %d", len(tc.expectedResult), len(tableColumns))
}
for i := range tableColumns {
if tableColumns[i] != tc.expectedResult[i] {
t.Errorf("Expected table column %v, but got %v", tc.expectedResult[i], tableColumns[i])
}
}
})
}
}
Здесь мы определяем набор тестовых примеров, каждый из которых имеет имя, имя схемы, имя таблицы, ожидаемые данные строк и ожидаемый результат.
Создание имитации соединения с базой данных: Внутри тестовой функции мы создаем новое имитационное подключение к базе данных с помощью sqlmock.New():
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("An error occurred while creating mock: %s", err)
}
defer db.Close()
Затем мы создаем экземпляр OracleConsumer с имитацией подключения к базе данных:
consumer := db_connection.New(db)
Определение ожидаемых строк и ожиданий запроса: Для каждого тестового случая нам необходимо определить ожидаемые строки, которые должны быть возвращены при вызове функции GetTableDetails:
expectedRows := sqlmock.NewRows([]string{"COLUMN_NAME", "DATA_TYPE"})
for _, row := range tc.expectedRowsData {
expectedRows.AddRow(row...)
}
Далее мы подготавливаем запрос, который будет перехвачен sqlmock. В этом примере мы используем константу db_connection.QueryGetTableDetails, которая содержит строку SQL-запроса:
mock.ExpectQuery(db_connection.QueryGetTableDetails).WithArgs(tc.tblName, tc.schemaName).WillReturnRows(expectedRows)
Вызов функции и проверка результатов: Теперь мы можем вызвать функцию GetTableDetails и проверить, соответствуют ли возвращаемые столбцы таблицы ожидаемому результату:
tableColumns, err := consumer.GetTableDetails(tc.schemaName, tc.tblName)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if len(tableColumns) != len(tc.expectedResult) {
t.Fatalf("Expected %d table columns, but got %d", len(tc.expectedResult), len(tableColumns))
}
for i := range tableColumns {
if tableColumns[i] != tc.expectedResult[i] {
t.Errorf("Expected table column %v, but got %v", tc.expectedResult[i], tableColumns[i])
}
}
Заключение
Используя sqlmock, вы можете легко создавать макеты соединений с базой данных и перехватывать SQL-запросы в ваших модульных тестах, что позволяет вам тестировать код изолированно, не полагаясь на реальную базу данных. Такой подход гарантирует, что ваши тесты будут более надежными, быстрыми и простыми в сопровождении.
В этом посте мы показали вам, как использовать sqlmock для имитации подключения к базе данных, определения ожидаемых строк, подготовки ожидаемых запросов и проверки результатов функций, связанных с базой данных. Обладая этими знаниями, вы сможете уверенно тестировать свой код, связанный с базой данных, и убедиться, что он работает так, как ожидается.