unit-testing - 来自测试,恐慌:sql:返回的连接从未断开
问题描述
我在测试运行时出现以下恐慌:
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
的只是一个[]*interface{}
大小为 3 的函数。简单地说,它是三个点Scan
。
与测试相关的其余定义在此辅助方法中定义,该方法在套件设置中调用:
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.
使用的第三方库
我使用testify
and go-sqlmock
(我可能会用它来简单地存根查询,因为我不得不在设置中跳过箍。)
我不知道是什么导致了这个失败。当我删除测试并运行套件本身时,一切正常
解决方案
原来是我多虑了这种情况。
我通过将两个行状态存储为指针来修复它,然后将它们设置为RowsSuite.SetupTest
(在测试级别,而不是套件级别)。从那里,我只是获取测试中的一个状态,我很高兴去。即,而不是这个(在 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))
}
}
我这样做了:
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
在运行测试之前设置的状态之一在哪里
推荐阅读
- android - 为什么 CollapsingToolbarLayout 在展开的内容下放置一个空白区域
- sql - 我想按 2 列和批量方式连续生成数字
- java - problem about installing bazel with homebrew
- javascript - React 生命周期方法作为类属性箭头函数
- thumbnails - 在 TYPO3 LTS9 中的图像文件夹库上裁剪和方形缩略图,点击放大时仍有原始图像?
- php - 在 IdentityPool 下使用 PHP CGI 的内置 IIS AD 身份验证
- hibernate - 防止休眠在插入数据库之前将即时时间转换为本地时间
- python-3.x - 根据python中的单词数进行文本分割
- javascript - 遍历数组以获取对象之间的距离
- react-native - 如何找到可能的未处理承诺拒绝的来源