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 для имитации подключения к базе данных, определения ожидаемых строк, подготовки ожидаемых запросов и проверки результатов функций, связанных с базой данных. Обладая этими знаниями, вы сможете уверенно тестировать свой код, связанный с базой данных, и убедиться, что он работает так, как ожидается.

+1
0
+1
1
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *