douzhun5971 2018-08-29 16:16
浏览 99

从测试中,恐慌:sql:从未返回的连接返回

I get the following panic at run time of my test:

panic: sql: connection returned that was never out

The test

The actual test is part of a test suite, and is defined thus:

func (suite *RowsSuite) TestReadFrom_SanityTest() {
    t := suite.T()

    rows := new(sql.Rows)
    *rows = suite.nonEmptyRows

    assert.True(t, rows.Next())
    // hit the function like it's supposed to be hit
    err := ReadFrom(rows,
        suite.fnStubFalse,
        suite.spots[0],
        suite.spots[1],
        suite.spots[2])

    // There better be no error
    if !assert.Nil(t, err) {
        t.Error(err.ToString(false))
    }

}

The code under test

I'm responsible for testing this function:

// ReadFrom reads values from `rows` into `valuePlaceholders`
func ReadFrom(rows *sql.Rows,
    readerFunc func(rowIndex int, readValues ...interface{}) (bool, *errors.ErrorSt),
    valuePlaceholders ...interface{}) (err *errors.ErrorSt) {

    rowLine := 1
    for rows.Next() {
        if err := rows.Scan(valuePlaceholders...); err != nil {
            return errors.Database().AddDetails(err.Error(), valuePlaceholders)
        }
        if readerFunc != nil {
            skipRest, err := readerFunc(rowLine, valuePlaceholders...)
            if err != nil {
                return err
            }
            if skipRest {
                break
            }
        }
        rowLine++
    }
    if rowLine == 1 {
        return errors.Get(appErrors.ACNoRows)
    }
    return nil
}

Setup

suite.fnStubFalse is simply a function stub that returns false,nil suite.spots is simply an []*interface{} of size 3. Simply put, it's three spots to Scan to.

The rest of the definitions relevant to the test are defined in this helper method which is invoked on suite setup:

func (suite *RowsSuite) setupRowStates() {
    // for throwing fatal error right away
    t := suite.T()
    // fire up the mock
    suite.db, suite.mock, suite.err = sqlmock.New()
    // if there's an error, fatally throw it right away!
    if !assert.Nilf(t,
        suite.err,
        "Error with initializing a stub database connection") {
        t.Fatal()
    }

    // define the possible expectant result sets
    noRows := sqlmock.NewRows(suite.columns)
    nonEmptyRows := sqlmock.NewRows(suite.columns).
        AddRow(381, "Beans", 1.59).
        AddRow(34981, "Frozen Pizza", 5.49)

    // define our sql behavior
    regex := "^SELECT (.+) FROM items$"
    sql := "SELECT (item_id, item_name, item_price) FROM items"

    specificRegex := "^SELECT (.+) FROM items (.+)$"
    specificSQL := `
    SELECT (item_id, item_name, item_price) FROM items i
    INNER JOIN stock s
    ON s.item_id = i.item_id
    WHERE TIME_TO_SEC(s.stock_time) > TIME_TO_SEC(NOW())`

    // setup general query to return non-empty rows
    suite.mock.ExpectQuery(regex).
        WillReturnRows(nonEmptyRows)
    // setup specific query to return empty rows
    suite.mock.ExpectQuery(specificRegex).
        WillReturnRows(noRows)

    // hit both queries right now and store the state of their
    //  return values, terminating right away on any errors
    var err error
    rows, err := suite.db.Query(sql)
    if err != nil {
        t.Fatal(err.Error())
    }
    suite.nonEmptyRows = *rows
    emptyRows, err := suite.db.Query(specificSQL)
    if err != nil {
        t.Fatal(err.Error())
    }
    suite.noRows = *emptyRows

}

The full error

This monstrosity:

Running tool: C:\Go\bin\go.exe test -timeout 30s ezsoft\apiserver_sdk\db -run ^TestRowsSuite$

panic: sql: connection returned that was never out

goroutine 22 [running]:
database/sql.(*DB).putConn(0xc04204d400, 0xc04212a080, 0x0, 0x0, 0xc04213de01)
    C:/Go/src/database/sql/sql.go:1158 +0x351
database/sql.(*driverConn).releaseConn(0xc04212a080, 0x0, 0x0)
    C:/Go/src/database/sql/sql.go:383 +0x53
database/sql.(*driverConn).(database/sql.releaseConn)-fm(0x0, 0x0)
    C:/Go/src/database/sql/sql.go:706 +0x45
database/sql.(*Rows).close(0xc04212a100, 0x899be0, 0xc042048380, 0x0, 0x0)
    C:/Go/src/database/sql/sql.go:2932 +0x159
database/sql.(*Rows).awaitDone(0xc04212a100, 0x89d260, 0xc042050b00, 0x0, 0x0)
    C:/Go/src/database/sql/sql.go:2588 +0x12f
created by database/sql.(*Rows).initContextClose
    C:/Go/src/database/sql/sql.go:2572 +0xa3
FAIL    ezsoft/apiserver_sdk/db 0.429s
Error: Tests failed.

Third-party libraries used

I use testify and go-sqlmock (I'm probably shooting myself in the foot by using it to simply stub queries, because I'm having to jump through hoops in the setup.)

I have no idea what's causing this fail. When I delete the test, and run the suite itself, everything works

  • 写回答

1条回答 默认 最新

  • dongtanzhu5417 2018-08-29 18:28
    关注

    Turns out that I was overthinking this situation.

    I fixed it by storing the two row states as pointers, and then setting them in RowsSuite.SetupTest (at the test level, not the suite level). From there, I just fetch one of the states in the test, and I was good to go. Namely, instead of this (which would work fine in C++):

    func (suite *RowsSuite) TestReadFrom_SanityTest() {
        t := suite.T()
    
        rows := new(sql.Rows)
        *rows = suite.nonEmptyRows
    
        assert.True(t, rows.Next())
        // hit the function like it's supposed to be hit
        err := ReadFrom(rows,
            suite.fnStubFalse,
            suite.spots[0],
            suite.spots[1],
            suite.spots[2])
    
        // There better be no error
        if !assert.Nil(t, err) {
            t.Error(err.ToString(false))
        }
    
    }
    

    I did this:

    func (suite *RowsSuite) TestReadFrom_SanityTest() {
        t := suite.T()
    
        rows := suite.nonEmptyRows
    
        // hit the function like it's supposed to be hit
        errSt := ReadFrom(rows,
            suite.fnStubFalse,
            suite.spots[0],
            suite.spots[1],
            suite.spots[2])
    
        // There better be no error
        if !assert.Nil(t, errSt) {
            t.Error(errSt.ToString(false))
        }
    
    }
    

    where nonEmptyRows is one of the states set just before the test is run

    评论

报告相同问题?

悬赏问题

  • ¥15 我的数据无法存进链表里
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大
  • ¥15 Oracle中如何从clob类型截取特定字符串后面的字符
  • ¥15 想通过pywinauto自动电机应用程序按钮,但是找不到应用程序按钮信息
  • ¥15 如何在炒股软件中,爬到我想看的日k线
  • ¥15 seatunnel 怎么配置Elasticsearch
  • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.
  • ¥15 (标签-MATLAB|关键词-多址)
  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端