简体   繁体   中英

How to correctly set Mock Row and Query for go-sqlmock

I'm setting up testing in golang .
I use go-sqlmock to test mysql connection.
But sqlmock.NewRows and mock.ExpectQuery does not work well with error.
I want to know how to resolve this error.

server side: golang
db: mysql
web framework: gin

dao.go

func GetSingleArticleDao(c *gin.Context, db *sql.DB) (util.Article, *sql.Rows) {
    id := c.Params.ByName("id")
    article := util.Article{}
    errArticle := db.QueryRow("SELECT * FROM articles WHERE id = ?", id).Scan(&article.ID, &article.UUID, &article.TITLE, &article.CONTENT)
    if errArticle != nil {
        panic(errArticle.Error())
    }
    rows, errImage := db.Query("SELECT image_name FROM images WHERE article_uuid  = ?", article.UUID)
    if errImage != nil {
        panic(errImage.Error())
    }

    return article, rows
}

dao_test.go

func TestGetSingleArticleDao(t *testing.T) {
    db, mock, err := sqlmock.New()

    if err != nil {
        t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
    }

    defer db.Close()

    articleMockRows := sqlmock.NewRows([]string{"id", "uuid", "title", "content"}).
        AddRow("1", "bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c", "test", "test")

    mock.ExpectQuery("SELECT (.+) FROM articles where id=\\?").
        WithArgs("1").
        WillReturnRows(articleMockRows)

    imageMockRows := sqlmock.NewRows([]string{"article_uuid", "image_name"}).
        AddRow("bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c", "1a90696f-4fe7-48f5-81a5-ca72c129f4b0").
        AddRow("bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c", "3d997272-468f-4b66-91db-00c39f0ef717")

    mock.ExpectQuery("^SELECT (.+) FROM images*").
        WithArgs("bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c").
        WillReturnRows(imageMockRows)

    resp := httptest.NewRecorder()
    gin.SetMode(gin.TestMode)
    ctx, _ := gin.CreateTestContext(resp)

    article, imageRows := GetSingleArticleDao(ctx, db)

    for imageRows.Next() {
        imageName := util.ImageName{}
        err := imageRows.Scan(&imageName.NAME)
        if err != nil {
            panic(err.Error())
        }
        article.IMAGENAMES = append(article.IMAGENAMES, imageName)
    }

    expectedArticle := util.Article{
        ID:      1,
        UUID:    "bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c",
        TITLE:   "test",
        CONTENT: "test",
    }

    imageName1 := util.ImageName{
        NAME: "1a90696f-4fe7-48f5-81a5-ca72c129f4b0",
    }
    expectedArticle.IMAGENAMES = append(expectedArticle.IMAGENAMES, imageName1)

    imageName2 := util.ImageName{
        NAME: "3d997272-468f-4b66-91db-00c39f0ef717",
    }

    expectedArticle.IMAGENAMES = append(expectedArticle.IMAGENAMES, imageName2)

    assert.Equal(t, expectedArticle, article)
}

I expect go test -v runs without error.
But the actual is not.
Here is the error.

$ go test -v 
=== RUN   TestGetSingleArticleDao
--- FAIL: TestGetSingleArticleDao (0.00s)
panic: Query: could not match actual sql: "SELECT * FROM articles WHERE id = ?" with expected regexp "SELECT (.+) FROM articles where id=\?" [recovered]
        panic: Query: could not match actual sql: "SELECT * FROM articles WHERE id = ?" with expected regexp "SELECT (.+) FROM articles where id=\?"

goroutine 34 [running]:
testing.tRunner.func1(0xc00022c000)
        /usr/local/go/src/testing/testing.go:830 +0x392
panic(0x165a960, 0xc0001bc4a0)
        /usr/local/go/src/runtime/panic.go:522 +0x1b5
article/api/dao.GetSingleArticleDao(0xc000214160, 0xc0002180c0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /Users/jpskgc/article/api/dao/dao.go:26 +0x35d
article/api/dao.TestGetSingleArticleDao(0xc00022c000)
        /Users/jpskgc/article/api/dao/dao_test.go:92 +0x750
testing.tRunner(0xc00022c000, 0x1788730)
        /usr/local/go/src/testing/testing.go:865 +0xc0
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:916 +0x35a
exit status 2
FAIL    article/api/dao 0.030s

This is caused by following part

    resp := httptest.NewRecorder()
    gin.SetMode(gin.TestMode)
    ctx, _ := gin.CreateTestContext(resp)

I should add "id" :"1" param in ctx.
So I changed that part into this:

    param := gin.Param{"id", "1"}
    params := gin.Params{param}
    req, _ := http.NewRequest("GET", "/article/1", nil)
    var context *gin.Context
    context = &gin.Context{Request: req, Params: params}

You might need to use regexp.QuoteMeta() to escape all regular expression metacharacters in your query instead of using \\ :

mock.ExpectQuery(regexp.QuoteMeta("SELECT (.+) FROM articles FROM articles WHERE id = ?")).
            WithArgs("1").
            WillReturnRows(articleMockRows)

Just a little tips, instead of doing it like this:

mock.ExpectQuery("SELECT (.+) FROM articles where id=\\?").
        WithArgs("1").
        WillReturnRows(articleMockRows)

You can try it by doing it like this:

mock.ExpectQuery("SELECT (.+) FROM articles").
        WithArgs("1").
        WillReturnRows(articleMockRows)

It depends on what you want to check in your query, but for me, the most important thing is to check whether my query selects the correct table or not. I mostly do not care which property is used to filter the data. If you think the filter is important, then, there are also some more examples here:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM