sqlmock не соответствует запросу, но запрос идентичен, и вывод журнала показывает то же самое - PullRequest
2 голосов
/ 08 января 2020

Я пытаюсь написать тесты для некоторого кода, используя Gorm, используя sqlmock. Я решил написать тесты для своей функции вставки, но теперь выдернул свои волосы, пытаясь заставить работать обновление.

Первая часть рабочего процесса просто запрашивает запись из базы данных. Я не могу заставить его совпадать с моим SQL, хотя вывод журнала показывает, что они идентичны.

Вот сообщение об ошибке:

(/path/to/my/project/database.go:263)
[2020-01-08 10:29:40]  Query: could not match actual sql: "SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1" with expected regexp "SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1"

Я также пытался использовать ExpectExe c вставка ExpectQuery.

    for _, c := range cases {

        db, mock, err := sqlmock.New()
        if err != nil {
            t.Fatal(err)
        }

        DB, err := gorm.Open("sqlite3", db)
        if err != nil {
            t.Fatal(err)
        }
        DB.LogMode(true)

        mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`)

        err = UpdateStoragePool(DB, &c.givenPool)
        if !reflect.DeepEqual(c.wantedError, err) {
            t.Fatalf("expecting errror %q, got %q", c.wantedError, err)
        }

        // if we didn't have any errors during the tx, check all expectations were met
        if c.wantedError == nil {
            if err := mock.ExpectationsWereMet(); err != nil {
                t.Fatalf(err.Error())
            }
        }

    }

Я также пробовал:

mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = '1')) ORDER BY "storage_pools"."id" ASC LIMIT 1`).WithArgs(1)  

mock.ExpectExec(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`)  

mock.ExpectExec(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = '1')) ORDER BY "storage_pools"."id" ASC LIMIT 1`).WithArgs(1)

У кого-нибудь есть идеи, что я здесь не так делаю?

* ОБНОВЛЕНИЕ *

По некоторым причинам это НЕ работает для операторов выбора:

        mock.ExpectExec(`SELECT \* FROM "storage_pools"`).
            WithArgs(c.givenPool.PoolId).WillReturnResult(sqlmock.NewResult(1, 1))
[2020-01-13 10:32:21]  call to Query 'SELECT * FROM "storage_pools"  WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1' with args [{Name: Ordinal:1 Value:1}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:
  - matches sql: 'SELECT \* FROM "storage_pools"'
  - is with arguments:
    0 - 1
  - should return Result having:
      LastInsertId: 1
      RowsAffected: 1

Это работает, но теперь я столкнулся с новой проблемой, где. Для начала Горм по какой-то причине делает 2 оператора выбора ... Первый работает и находит строку, второй запрос не находит ту же строку. Я в недоумении здесь. Собираюсь просто отказаться от этой библиотеки. Я мог бы написать свой за время, которое мы потратили, пытаясь заставить его работать.

        db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
        if err != nil {
            t.Fatal(err)
        }

        DB, err := gorm.Open("postgres", db)
        if err != nil {
            t.Fatal(err)
        }
        DB.LogMode(true)

        mockedRow := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")

        // Mock the complete transaction
        mock.ExpectQuery(`SELECT * FROM "storage_pools"  WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`).
            WithArgs(c.givenPool.PoolId).
            WillReturnRows(mockedRow)

        mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND "storage_pools"."id" = ? AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC`).
            WithArgs(1, c.givenPool.PoolId).
            WillReturnRows(mockedRow)

Ответы [ 2 ]

4 голосов
/ 09 января 2020

mock.ExpectExec() функция не выполняет сравнение простых строк. Вместо этого он использует входную строку в качестве RegExp для соответствия запросу.

Некоторые символы в вашей SQL строке соответствия являются зарезервированными символами RegExp и должны быть экранированы для соответствия SQL.

Ваша строка должна после экранирования выглядит так:

SELECT \* FROM "storage_pools" WHERE "storage_pools"\."deleted_at" IS NULL AND \(\(poolid \= \?\)\) ORDER BY "storage_pools"\."id" ASC LIMIT 1

Подсказка: вы можете экранировать свою строку в Интернете, используя https://www.regex-escape.com/preg_quote-online.php или другой сайт

Дополнительно мысль: тест с точным совпадением SQL может быть fr agile без добавления дополнительного значения для точного SQL.

Тест может дать вам ложноположительный результат, если кто-либо внес безобидные изменения в него, например, добавив дополнительный пробел. С другой стороны, при полнотекстовом сопоставлении не фиксируются изменения схемы БД, которые не совместимы с SQL.

. В результате я получил эту настройку для своих проектов:

Запуск модульных тестов с mock.ExpectExec() с базовыми c подстроками типа INSERT INTO history. Это делает тесты намного менее приятными. В то же время мы все еще проверяем много в этом тесте, чтобы проверить поток выполнения кода:

  1. Количество SQL параметров
  2. Значения этих SQL параметров
  3. Убедитесь, что команда SQL выполнена с использованием mock.ExpectationsWereMet ()

Кроме того, мы должны запустить интеграционные тесты для наших SQL запросов. Это единственный способ убедиться, что наши SQL верны и соответствуют последним изменениям БД.

PS Избегайте * при выборе. Будьте явны с именами полей.

Update1:

Будьте осторожны с регистром строк. «SELECT» и «select» - это две разные строки.

Некоторые фрагменты кода из моего текущего проекта:

// insert
sqlMock.ExpectExec("INSERT INTO eeo").
        WithArgs("2018-12-31", "John Dow", "title"}).
        WillReturnResult(sqlmock.NewResult(mock.EeoID, 1))

// select
rows := sqlmock.NewRows([]string{"req_id", "state"})
sqlMock.ExpectQuery("select").WithArgs(mock.CandidateID).WillReturnRows(rows)
0 голосов
/ 05 апреля 2020

Это странное решение, но у меня сработало. Вероятно, ошибка в sqlmock. Дублируйте вашу переменную mockedRow и вставьте их в ExpectQuery.

mockedRow := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")
mockedRow2 := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")

        // Mock the complete transaction
        mock.ExpectQuery(`SELECT * FROM "storage_pools"  WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`).
            WithArgs(c.givenPool.PoolId).
            WillReturnRows(mockedRow)

        mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND "storage_pools"."id" = ? AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC`).
            WithArgs(1, c.givenPool.PoolId).
            WillReturnRows(mockedRow2)

В качестве альтернативы вы можете создать массив mockedRow следующим образом:

mockedRow := []*sqlmock.Rows{
sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1"),
sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1"),
}
And use it as WillReturnRows(mockedRow[0]) and WillReturnRows(mockedRow[1])
...