Unit testing 从测试中,panic:sql:connection返回了从未中断的连接

Unit testing 从测试中,panic:sql:connection返回了从未中断的连接,unit-testing,go,Unit Testing,Go,我在测试运行时遇到以下恐慌: panic: sql: connection returned that was never out 测试 实际试验是试验的一部分,其定义如下: func (suite *RowsSuite) TestReadFrom_SanityTest() { t := suite.T() rows := new(sql.Rows) *rows = suite.nonEmptyRows assert.True(t, rows.Next())

我在测试运行时遇到以下恐慌:

panic: sql: connection returned that was never out
测试

实际试验是试验的一部分,其定义如下:

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))
    }

}
正在测试的代码

我负责测试此功能:

// 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
}
设置

suite.fnStubFalse
只是一个函数存根,返回
false,nil
suite.spots
只是一个大小为3的
[]*接口{}
。简单地说,它有三个点要扫描

与测试相关的其余定义在该助手方法中定义,该方法在套件设置中调用:

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

}
完整错误

这个怪物:

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.
使用的第三方库

我使用and(我可能会用它来简单地存根查询,这是在打击我自己,因为我必须在设置中跳转。)


我不知道是什么导致了这次失败。当我删除测试并运行套件本身时,一切正常

结果表明我过度考虑了这种情况

我通过将两行状态存储为指针,然后在
rowssite.SetupTest
中设置它们(在测试级别,而不是套件级别)来修复它。从那里,我只得到了测试中的一个状态,我很乐意去。也就是说,与此相反(在C++中可以很好地工作):

我这样做:

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))
    }

}

其中,
nonEmptyRows
是在测试运行之前设置的状态之一

如果有帮助,则出错的错误在这里的go源代码中:这意味着,不知何故,某个东西正在返回到数据库连接池的连接,而该连接从未断开(发出)。或者可能它已分发,但从未投入使用。既然您提到了这一点,我将尝试执行以下操作:-在安装程序中,将两个不同的行状态设置为
套件
的成员。为了做到这一点,并避免以后进行浅层复制,我将它们保存为
sql.Row
,而不是
*sql.Row
——在测试中,我尝试通过设置一个新指针,并设置它指向原始状态的内容,对其中一个(我想要的)进行深度复制。这可能会将正在执行操作的
*sql.Row
与套件中的任何存根连接分离,从而导致可能就是它的恐慌。我不建议尝试以这种方式克隆这些资源,因为
sql
正试图进行幕后资源管理。对于这种情况,是否有其他选择?啊哈,通过手动创建
new(sql.Rows)
它是用零值创建的,会引起恐慌。我猜你不能在没有正确初始化的情况下创建另一个包结构,这是有道理的,但可能会忘记。
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))
    }

}