使用gorm和sqlmock进行测试

huangapple go评论79阅读模式
英文:

Testing with gorm and sqlmock

问题

我正在努力为我的Go Lambda函数编写一个使用sqlmock和gorm进行测试的测试。

这是我想要测试的函数:

func DoCleanup(con *gorm.DB) {
    sixMonthsAgo := time.Now().AddDate(0, -6, 0).Format("2006-02-01")
    con.Where("date_to <= ?", sixMonthsAgo).Delete(&Availability{})
    con.Where("date_to <= ?", sixMonthsAgo).Delete(&Reservation{})
}

这是我的测试:

func TestDoCleanup(m *testing.T) {
    var mock sqlmock.Sqlmock
    var db *sql.DB
    var err error

    db, mock, err = sqlmock.New()
    assert.Nil(m, err)

    dialector := mysql.New(mysql.Config{
        DSN:                       "sqlmock_db_0",
        DriverName:                "mysql",
        Conn:                      db,
        SkipInitializeWithVersion: true,
    })

    conn, err := gorm.Open(dialector, &gorm.Config{})
    if err != nil {
        m.Errorf("Failed to open connection to DB: %v", err)
    }

    if conn == nil {
        m.Error("Failed to open connection to DB: conn is nil")
    }

    defer db.Close()

    mock.ExpectQuery(fmt.Sprintf("DELETE FROM availability WHERE date_to <= '%s'", time.Now().AddDate(0, -6, 0).Format("2006-02-01")))
    mock.ExpectQuery(fmt.Sprintf("DELETE FROM reservations WHERE date_to <= '%s'", time.Now().AddDate(0, -6, 0).Format("2006-02-01")))

    DoCleanup(conn)

    err = mock.ExpectationsWereMet()
    assert.Nil(m, err)
}

我不知道我做错了什么。这是我第一次使用sqlmock。我在几个地方阅读了一些资料,我的代码看起来没问题,但是我没有得到结果。我的错误是:

Expected nil, but got: &errors.errorString{s:"there is a remaining expectation which was not matched: ExpectedQuery => expecting Query, QueryContext or QueryRow which:\n  - matches sql: 'DELETE FROM availability WHERE date_to <= '2022-13-06''\n  - is without arguments"}

有什么想法我做错了什么吗?

英文:

I am struggling with writing a test for my go lambda function with sqlmock and gorm.

This is the function I want to test:

func DoCleanup(con *gorm.DB) {
    sixMonthsAgo := time.Now().AddDate(0, -6, 0).Format(&quot;2006-02-01&quot;)
	con.Where(&quot;date_to &lt;= ?&quot;, sixMonthsAgo).Delete(&amp;Availability{})
	con.Where(&quot;date_to &lt;= ?&quot;, sixMonthsAgo).Delete(&amp;Reservation{})
}

And this is my test:

func TestDoCleanup(m *testing.T) {
	var mock sqlmock.Sqlmock
	var db *sql.DB
	var err error

	db, mock, err = sqlmock.New()
	assert.Nil(m, err)

	dialector := mysql.New(mysql.Config{
		DSN:                       &quot;sqlmock_db_0&quot;,
		DriverName:                &quot;mysql&quot;,
		Conn:                      db,
		SkipInitializeWithVersion: true,
	})

	conn, err := gorm.Open(dialector, &amp;gorm.Config{})
	if err != nil {
		m.Errorf(&quot;Failed to open connection to DB: %v&quot;, err)
	}

	if conn == nil {
		m.Error(&quot;Failed to open connection to DB: conn is nil&quot;)
	}

	defer db.Close()

    mock.ExpectQuery(fmt.Sprintf(&quot;DELETE FROM availability WHERE date_to &lt;= &#39;%s&#39;&quot;, time.Now().AddDate(0, -6, 0).Format(&quot;2006-02-01&quot;)))
    mock.ExpectQuery(fmt.Sprintf(&quot;DELETE FROM reservations WHERE date_to &lt;= &#39;%s&#39;&quot;, time.Now().AddDate(0, -6, 0).Format(&quot;2006-02-01&quot;)))

	DoCleanup(conn)

	err = mock.ExpectationsWereMet()
	assert.Nil(m, err)

}

I don't know what I am doing wrong. This is the first time I'm using sqlmock. I've read up a few places, and my code looks fine, but I'm not getting the results. My error is:

Expected nil, but got: &amp;errors.errorString{s:&quot;there is a remaining expectation which was not matched: ExpectedQuery =&gt; expecting Query, QueryContext or QueryRow which:\n  - matches sql: &#39;DELETE FROM availability WHERE date_to &lt;= &#39;2022-13-06&#39;&#39;\n  - is without arguments&quot;}

Any ideas what I'm doing wrong?

答案1

得分: 1

我看到的主要问题是你对查询的期望方式不正确。你应该这样写:

	mock.ExpectBegin()
	mock.ExpectExec("DELETE FROM `availability` WHERE date_to <= ?").
		WithArgs(time.Now().AddDate(0, -6, 0).Format("2006-02-01")).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectCommit()

这样告诉模拟对象你正在使用一个事务(在删除操作周围使用了ExpectBeginExpectCommit),查询是带有参数的(使用了WithArgs),以及查询的返回结果是什么(使用了WillReturnResult)。

还有一些其他的小改动,比如表名周围的``(MySQL 的习惯用法),以及表名的命名(gorm 通常会将表名转为复数形式,所以要么你在 Availability 上实现了 TableName 方法,要么它会默认为 availabilities)。

你可以将 DoCleanup 函数改为返回错误,并在测试中查看错误信息,这样可以更好地发现所有这些问题:

func DoCleanup(con *gorm.DB) error {
	sixMonthsAgo := time.Now().AddDate(0, -6, 0).Format("2006-02-01")
	tx := con.Where("date_to <= ?", sixMonthsAgo).Delete(&Availability{})
	if tx.Error != nil {
		return tx.Error
	}
	tx = con.Where("date_to <= ?", sixMonthsAgo).Delete(&Reservation{})
	if tx.Error != nil {
		return tx.Error
	}
	return nil
}
...
err = DoCleanup(conn)
assert.Nil(m, err)
...

这样做,并使用当前的代码,你会得到以下错误信息:

Expected nil, but got: &errors.errorString{s:"call to database transaction Begin, was not expected, next expectation is: ExpectedQuery => expecting Query, QueryContext or QueryRow which:\n  - matches sql: 'DELETE FROM availability WHERE date_to <= '2022-13-06''\n  - is without arguments"}

这告诉你 sqlmock 不希望出现 Begin,解决了这个问题后,你会得到其他错误信息,这些错误在上面的回答的前半部分中已经解决了。

英文:

the main problem I see is around the way you are expecting the Query to be. Instead of

    mock.ExpectQuery(fmt.Sprintf(&quot;DELETE FROM availability WHERE date_to &lt;= &#39;%s&#39;&quot;, time.Now().AddDate(0, -6, 0).Format(&quot;2006-02-01&quot;)))

you should have:

	mock.ExpectBegin()
	mock.ExpectExec(&quot;DELETE FROM `availability` WHERE date_to &lt;= ?&quot;).
		WithArgs(time.Now().AddDate(0, -6, 0).Format(&quot;2006-02-01&quot;)).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectCommit()

This will tell the mocking that you are using a transaction (ExpectBegin and ExpectCommit around the delete, that the query was made with an argument(WithArgs), and what the return of the query will be (WillReturnResult)

There are some other minor changes, like the `` around the table name (a MySQL idiom) and the name of the table (gorm will typically pluralize names so either you implemented TableName on Availability or it will default to availabilities).

The best way for you to see all these problems would be to change the DoCleanup to return errors and then look at them on the test:

func DoCleanup(con *gorm.DB) error {
	sixMonthsAgo := time.Now().AddDate(0, -6, 0).Format(&quot;2006-02-01&quot;)
	tx := con.Where(&quot;date_to &lt;= ?&quot;, sixMonthsAgo).Delete(&amp;Availability{})
	if tx.Error != nil {
		return tx.Error
	}
	tx = con.Where(&quot;date_to &lt;= ?&quot;, sixMonthsAgo).Delete(&amp;Reservation{})
	if tx.Error != nil {
		return tx.Error
	}
	return nil
}
...
	err = DoCleanup(conn)
	assert.Nil(m, err)
...

Doing this, and with the current code you would get

Expected nil, but got: &amp;errors.errorString{s:&quot;call to database transaction Begin, was not expected, next expectation is: ExpectedQuery =&gt; expecting Query, QueryContext or QueryRow which:\n  - matches sql: &#39;DELETE FROM availability WHERE date_to &lt;= &#39;2022-13-06&#39;&#39;\n  - is without arguments&quot;}

This tells you that sqlmock was not expecting a Begin, after solving that you will get the other errors that are resolved in the first part of this answer.

huangapple
  • 本文由 发表于 2022年12月9日 20:40:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/74743303.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定