2014-02-07 11:44:41 +03:00
/ *
2016-01-21 21:09:44 +02:00
Package sqlmock is a mock library implementing sql driver . Which has one and only
purpose - to simulate any sql driver behavior in tests , without needing a real
database connection . It helps to maintain correct * * TDD * * workflow .
2015-08-26 15:59:28 +02:00
It does not require any modifications to your source code in order to test
2016-01-21 21:09:44 +02:00
and mock database operations . Supports concurrency and multiple database mocking .
2015-08-26 15:59:28 +02:00
2016-01-21 21:09:44 +02:00
The driver allows to mock any sql driver method behavior .
2014-02-07 11:44:41 +03:00
* /
2014-02-05 17:21:07 +03:00
package sqlmock
import (
2015-09-10 20:31:35 +02:00
"database/sql"
2014-02-05 17:21:07 +03:00
"database/sql/driver"
"fmt"
"regexp"
2017-02-06 14:38:57 +02:00
"time"
2014-02-05 17:21:07 +03:00
)
2015-08-28 10:06:14 +02:00
// Sqlmock interface serves to create expectations
// for any kind of database action in order to mock
// and test real database behavior.
type Sqlmock interface {
// ExpectClose queues an expectation for this database
// action to be triggered. the *ExpectedClose allows
// to mock database response
ExpectClose ( ) * ExpectedClose
// ExpectationsWereMet checks whether all queued expectations
// were met in order. If any of them was not met - an error is returned.
ExpectationsWereMet ( ) error
// ExpectPrepare expects Prepare() to be called with sql query
// which match sqlRegexStr given regexp.
// the *ExpectedPrepare allows to mock database response.
// Note that you may expect Query() or Exec() on the *ExpectedPrepare
// statement to prevent repeating sqlRegexStr
ExpectPrepare ( sqlRegexStr string ) * ExpectedPrepare
// ExpectQuery expects Query() or QueryRow() to be called with sql query
// which match sqlRegexStr given regexp.
// the *ExpectedQuery allows to mock database response.
ExpectQuery ( sqlRegexStr string ) * ExpectedQuery
// ExpectExec expects Exec() to be called with sql query
// which match sqlRegexStr given regexp.
// the *ExpectedExec allows to mock database response
ExpectExec ( sqlRegexStr string ) * ExpectedExec
// ExpectBegin expects *sql.DB.Begin to be called.
// the *ExpectedBegin allows to mock database response
ExpectBegin ( ) * ExpectedBegin
// ExpectCommit expects *sql.Tx.Commit to be called.
// the *ExpectedCommit allows to mock database response
ExpectCommit ( ) * ExpectedCommit
// ExpectRollback expects *sql.Tx.Rollback to be called.
// the *ExpectedRollback allows to mock database response
ExpectRollback ( ) * ExpectedRollback
2015-08-26 13:28:01 +02:00
// MatchExpectationsInOrder gives an option whether to match all
// expectations in the order they were set or not.
//
// By default it is set to - true. But if you use goroutines
// to parallelize your query executation, that option may
// be handy.
2015-08-28 10:06:14 +02:00
MatchExpectationsInOrder ( bool )
}
2015-08-26 13:28:01 +02:00
2015-08-28 10:06:14 +02:00
type sqlmock struct {
ordered bool
dsn string
opened int
drv * mockDriver
2014-02-05 17:21:07 +03:00
2015-07-17 12:14:30 +02:00
expected [ ] expectation
2014-02-05 17:21:07 +03:00
}
2016-01-21 21:09:44 +02:00
func ( c * sqlmock ) open ( ) ( * sql . DB , Sqlmock , error ) {
db , err := sql . Open ( "sqlmock" , c . dsn )
2015-09-10 20:31:35 +02:00
if err != nil {
2016-01-21 21:09:44 +02:00
return db , c , err
2015-09-10 20:31:35 +02:00
}
2016-01-21 21:09:44 +02:00
return db , c , db . Ping ( )
2015-09-10 20:31:35 +02:00
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectClose ( ) * ExpectedClose {
2015-07-17 12:14:30 +02:00
e := & ExpectedClose { }
c . expected = append ( c . expected , e )
return e
2014-02-05 17:21:07 +03:00
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) MatchExpectationsInOrder ( b bool ) {
c . ordered = b
}
2015-07-17 12:14:30 +02:00
// Close a mock database driver connection. It may or may not
// be called depending on the sircumstances, but if it is called
// there must be an *ExpectedClose expectation satisfied.
// meets http://golang.org/pkg/database/sql/driver/#Conn interface
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) Close ( ) error {
2015-07-17 12:14:30 +02:00
c . drv . Lock ( )
defer c . drv . Unlock ( )
c . opened --
if c . opened == 0 {
delete ( c . drv . conns , c . dsn )
}
2015-08-26 13:28:01 +02:00
var expected * ExpectedClose
2015-08-26 15:59:28 +02:00
var fulfilled int
2015-08-26 13:28:01 +02:00
var ok bool
for _ , next := range c . expected {
next . Lock ( )
if next . fulfilled ( ) {
next . Unlock ( )
2015-08-26 15:59:28 +02:00
fulfilled ++
2015-08-26 13:28:01 +02:00
continue
}
if expected , ok = next . ( * ExpectedClose ) ; ok {
break
}
next . Unlock ( )
2015-08-28 10:06:14 +02:00
if c . ordered {
2015-08-26 15:59:28 +02:00
return fmt . Errorf ( "call to database Close, was not expected, next expectation is: %s" , next )
2015-08-26 13:28:01 +02:00
}
}
2015-08-26 15:59:28 +02:00
2015-08-26 13:28:01 +02:00
if expected == nil {
2015-08-26 15:59:28 +02:00
msg := "call to database Close was not expected"
if fulfilled == len ( c . expected ) {
msg = "all expectations were already fulfilled, " + msg
}
return fmt . Errorf ( msg )
2015-07-17 12:14:30 +02:00
}
2015-08-26 13:28:01 +02:00
expected . triggered = true
expected . Unlock ( )
return expected . err
2014-02-05 17:21:07 +03:00
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectationsWereMet ( ) error {
2015-07-17 12:14:30 +02:00
for _ , e := range c . expected {
if ! e . fulfilled ( ) {
2015-08-26 15:59:28 +02:00
return fmt . Errorf ( "there is a remaining expectation which was not matched: %s" , e )
2015-07-17 12:14:30 +02:00
}
2014-04-21 18:21:28 +03:00
}
2015-07-17 12:14:30 +02:00
return nil
2014-04-21 18:21:28 +03:00
}
2015-07-17 12:14:30 +02:00
// Begin meets http://golang.org/pkg/database/sql/driver/#Conn interface
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) Begin ( ) ( driver . Tx , error ) {
2017-02-07 12:20:08 +02:00
ex , err := c . beginExpectation ( )
if err != nil {
return nil , err
}
return c . begin ( ex )
}
func ( c * sqlmock ) begin ( expected * ExpectedBegin ) ( driver . Tx , error ) {
defer time . Sleep ( expected . delay )
return c , nil
}
func ( c * sqlmock ) beginExpectation ( ) ( * ExpectedBegin , error ) {
2015-08-26 13:28:01 +02:00
var expected * ExpectedBegin
var ok bool
2015-08-26 15:59:28 +02:00
var fulfilled int
2015-08-26 13:28:01 +02:00
for _ , next := range c . expected {
next . Lock ( )
if next . fulfilled ( ) {
next . Unlock ( )
2015-08-26 15:59:28 +02:00
fulfilled ++
2015-08-26 13:28:01 +02:00
continue
}
if expected , ok = next . ( * ExpectedBegin ) ; ok {
break
}
next . Unlock ( )
2015-08-28 10:06:14 +02:00
if c . ordered {
2015-08-26 15:59:28 +02:00
return nil , fmt . Errorf ( "call to database transaction Begin, was not expected, next expectation is: %s" , next )
2015-08-26 13:28:01 +02:00
}
}
if expected == nil {
2015-08-26 15:59:28 +02:00
msg := "call to database transaction Begin was not expected"
if fulfilled == len ( c . expected ) {
msg = "all expectations were already fulfilled, " + msg
}
return nil , fmt . Errorf ( msg )
2015-07-17 12:14:30 +02:00
}
2015-08-26 13:28:01 +02:00
expected . triggered = true
expected . Unlock ( )
2017-02-07 12:20:08 +02:00
return expected , expected . err
2014-02-05 17:21:07 +03:00
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectBegin ( ) * ExpectedBegin {
2015-07-17 12:14:30 +02:00
e := & ExpectedBegin { }
c . expected = append ( c . expected , e )
return e
2014-02-05 17:21:07 +03:00
}
2015-07-17 12:14:30 +02:00
// Exec meets http://golang.org/pkg/database/sql/driver/#Execer
2017-02-06 14:38:57 +02:00
func ( c * sqlmock ) Exec ( query string , args [ ] driver . Value ) ( driver . Result , error ) {
namedArgs := make ( [ ] namedValue , len ( args ) )
for i , v := range args {
namedArgs [ i ] = namedValue {
Ordinal : i + 1 ,
Value : v ,
}
}
2017-02-07 12:20:08 +02:00
ex , err := c . execExpectation ( query , namedArgs )
if err != nil {
return nil , err
}
return c . exec ( ex )
2017-02-06 14:38:57 +02:00
}
2017-02-07 12:20:08 +02:00
func ( c * sqlmock ) execExpectation ( query string , args [ ] namedValue ) ( * ExpectedExec , error ) {
2015-07-17 12:14:30 +02:00
query = stripQuery ( query )
2015-08-26 13:28:01 +02:00
var expected * ExpectedExec
2015-08-26 15:59:28 +02:00
var fulfilled int
2015-08-26 13:28:01 +02:00
var ok bool
for _ , next := range c . expected {
next . Lock ( )
if next . fulfilled ( ) {
next . Unlock ( )
2015-08-26 15:59:28 +02:00
fulfilled ++
2015-08-26 13:28:01 +02:00
continue
}
2015-08-28 10:06:14 +02:00
if c . ordered {
2015-08-26 13:28:01 +02:00
if expected , ok = next . ( * ExpectedExec ) ; ok {
break
}
next . Unlock ( )
2015-08-26 15:59:28 +02:00
return nil , fmt . Errorf ( "call to exec query '%s' with args %+v, was not expected, next expectation is: %s" , query , args , next )
2015-08-26 13:28:01 +02:00
}
if exec , ok := next . ( * ExpectedExec ) ; ok {
2016-02-23 11:14:34 +02:00
if err := exec . attemptMatch ( query , args ) ; err == nil {
2015-08-26 13:28:01 +02:00
expected = exec
break
}
}
next . Unlock ( )
}
if expected == nil {
2015-08-26 15:59:28 +02:00
msg := "call to exec '%s' query with args %+v was not expected"
if fulfilled == len ( c . expected ) {
msg = "all expectations were already fulfilled, " + msg
}
return nil , fmt . Errorf ( msg , query , args )
2015-07-17 12:14:30 +02:00
}
2017-02-07 12:20:08 +02:00
defer expected . Unlock ( )
2015-07-17 12:14:30 +02:00
2015-08-26 13:28:01 +02:00
if ! expected . queryMatches ( query ) {
return nil , fmt . Errorf ( "exec query '%s', does not match regex '%s'" , query , expected . sqlRegex . String ( ) )
2015-07-17 12:14:30 +02:00
}
2016-02-23 11:14:34 +02:00
if err := expected . argsMatches ( args ) ; err != nil {
return nil , fmt . Errorf ( "exec query '%s', arguments do not match: %s" , query , err )
2015-07-17 12:14:30 +02:00
}
2016-02-26 18:07:19 +02:00
expected . triggered = true
2015-08-26 13:28:01 +02:00
if expected . err != nil {
return nil , expected . err // mocked to return error
2015-07-17 12:14:30 +02:00
}
2015-08-26 13:28:01 +02:00
if expected . result == nil {
return nil , fmt . Errorf ( "exec query '%s' with args %+v, must return a database/sql/driver.result, but it was not set for expectation %T as %+v" , query , args , expected , expected )
2015-07-17 12:14:30 +02:00
}
2016-02-26 18:07:19 +02:00
2017-02-07 12:20:08 +02:00
return expected , nil
}
func ( c * sqlmock ) exec ( expected * ExpectedExec ) ( driver . Result , error ) {
defer time . Sleep ( expected . delay )
return expected . result , nil
2014-02-05 17:21:07 +03:00
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectExec ( sqlRegexStr string ) * ExpectedExec {
2015-07-17 12:14:30 +02:00
e := & ExpectedExec { }
2017-02-04 13:09:50 +02:00
sqlRegexStr = stripQuery ( sqlRegexStr )
2015-07-17 12:14:30 +02:00
e . sqlRegex = regexp . MustCompile ( sqlRegexStr )
c . expected = append ( c . expected , e )
return e
2014-09-24 00:38:15 +03:00
}
2015-07-17 12:14:30 +02:00
// Prepare meets http://golang.org/pkg/database/sql/driver/#Conn interface
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) Prepare ( query string ) ( driver . Stmt , error ) {
2017-02-07 12:20:08 +02:00
ex , err := c . prepareExpectation ( query )
if err != nil {
return nil , err
}
return c . prepare ( ex , query )
}
func ( c * sqlmock ) prepareExpectation ( query string ) ( * ExpectedPrepare , error ) {
2015-08-26 13:28:01 +02:00
var expected * ExpectedPrepare
2015-08-26 15:59:28 +02:00
var fulfilled int
2015-08-26 13:28:01 +02:00
var ok bool
for _ , next := range c . expected {
next . Lock ( )
if next . fulfilled ( ) {
next . Unlock ( )
2015-08-26 15:59:28 +02:00
fulfilled ++
2015-08-26 13:28:01 +02:00
continue
}
2015-07-17 12:14:30 +02:00
2015-08-26 13:28:01 +02:00
if expected , ok = next . ( * ExpectedPrepare ) ; ok {
break
}
next . Unlock ( )
2015-08-28 10:06:14 +02:00
if c . ordered {
2016-04-13 21:19:21 +02:00
return nil , fmt . Errorf ( "call to Prepare statement with query '%s', was not expected, next expectation is: %s" , query , next )
2015-08-26 13:28:01 +02:00
}
2015-07-17 12:14:30 +02:00
}
2015-08-26 13:28:01 +02:00
query = stripQuery ( query )
if expected == nil {
2015-08-26 15:59:28 +02:00
msg := "call to Prepare '%s' query was not expected"
if fulfilled == len ( c . expected ) {
msg = "all expectations were already fulfilled, " + msg
}
return nil , fmt . Errorf ( msg , query )
2015-07-17 12:14:30 +02:00
}
2017-02-07 12:20:08 +02:00
defer expected . Unlock ( )
2016-11-02 14:49:59 +02:00
if ! expected . sqlRegex . MatchString ( query ) {
return nil , fmt . Errorf ( "query '%s', does not match regex [%s]" , query , expected . sqlRegex . String ( ) )
}
2015-07-17 12:14:30 +02:00
2015-08-26 13:28:01 +02:00
expected . triggered = true
2017-02-07 12:20:08 +02:00
return expected , expected . err
}
func ( c * sqlmock ) prepare ( expected * ExpectedPrepare , query string ) ( driver . Stmt , error ) {
2017-02-06 14:38:57 +02:00
defer time . Sleep ( expected . delay )
2017-02-07 12:20:08 +02:00
return & statement { c , query , expected . closeErr } , nil
2014-02-05 17:21:07 +03:00
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectPrepare ( sqlRegexStr string ) * ExpectedPrepare {
2017-02-04 13:09:50 +02:00
sqlRegexStr = stripQuery ( sqlRegexStr )
2015-07-17 12:14:30 +02:00
e := & ExpectedPrepare { sqlRegex : regexp . MustCompile ( sqlRegexStr ) , mock : c }
c . expected = append ( c . expected , e )
return e
2014-02-05 17:21:07 +03:00
}
2017-02-06 14:38:57 +02:00
type namedValue struct {
Name string
Ordinal int
Value driver . Value
}
2015-07-17 12:14:30 +02:00
// Query meets http://golang.org/pkg/database/sql/driver/#Queryer
2017-02-07 12:20:08 +02:00
func ( c * sqlmock ) Query ( query string , args [ ] driver . Value ) ( driver . Rows , error ) {
2017-02-06 14:38:57 +02:00
namedArgs := make ( [ ] namedValue , len ( args ) )
for i , v := range args {
namedArgs [ i ] = namedValue {
Ordinal : i + 1 ,
Value : v ,
}
}
2017-02-07 12:20:08 +02:00
ex , err := c . queryExpectation ( query , namedArgs )
if err != nil {
return nil , err
}
return c . query ( ex )
2017-02-06 14:38:57 +02:00
}
2017-02-07 12:20:08 +02:00
func ( c * sqlmock ) queryExpectation ( query string , args [ ] namedValue ) ( * ExpectedQuery , error ) {
2015-07-17 12:14:30 +02:00
query = stripQuery ( query )
2015-08-26 13:28:01 +02:00
var expected * ExpectedQuery
2015-08-26 15:59:28 +02:00
var fulfilled int
2015-08-26 13:28:01 +02:00
var ok bool
for _ , next := range c . expected {
next . Lock ( )
if next . fulfilled ( ) {
next . Unlock ( )
2015-08-26 15:59:28 +02:00
fulfilled ++
2015-08-26 13:28:01 +02:00
continue
}
2015-08-28 10:06:14 +02:00
if c . ordered {
2015-08-26 13:28:01 +02:00
if expected , ok = next . ( * ExpectedQuery ) ; ok {
break
}
next . Unlock ( )
2015-08-26 15:59:28 +02:00
return nil , fmt . Errorf ( "call to query '%s' with args %+v, was not expected, next expectation is: %s" , query , args , next )
2015-08-26 13:28:01 +02:00
}
if qr , ok := next . ( * ExpectedQuery ) ; ok {
2016-02-23 11:14:34 +02:00
if err := qr . attemptMatch ( query , args ) ; err == nil {
2015-08-26 13:28:01 +02:00
expected = qr
break
}
}
next . Unlock ( )
}
2015-08-26 15:59:28 +02:00
2015-08-26 13:28:01 +02:00
if expected == nil {
2015-08-26 15:59:28 +02:00
msg := "call to query '%s' with args %+v was not expected"
if fulfilled == len ( c . expected ) {
msg = "all expectations were already fulfilled, " + msg
}
return nil , fmt . Errorf ( msg , query , args )
2015-07-17 12:14:30 +02:00
}
2017-02-07 12:20:08 +02:00
defer expected . Unlock ( )
2015-08-26 13:28:01 +02:00
if ! expected . queryMatches ( query ) {
return nil , fmt . Errorf ( "query '%s', does not match regex [%s]" , query , expected . sqlRegex . String ( ) )
2015-07-17 12:14:30 +02:00
}
2016-02-23 11:14:34 +02:00
if err := expected . argsMatches ( args ) ; err != nil {
return nil , fmt . Errorf ( "exec query '%s', arguments do not match: %s" , query , err )
2015-07-17 12:14:30 +02:00
}
2016-02-26 18:07:19 +02:00
expected . triggered = true
2015-08-26 13:28:01 +02:00
if expected . err != nil {
return nil , expected . err // mocked to return error
2015-07-17 12:14:30 +02:00
}
2015-08-26 13:28:01 +02:00
if expected . rows == nil {
return nil , fmt . Errorf ( "query '%s' with args %+v, must return a database/sql/driver.rows, but it was not set for expectation %T as %+v" , query , args , expected , expected )
2015-07-17 12:14:30 +02:00
}
2017-02-07 12:20:08 +02:00
return expected , nil
}
func ( c * sqlmock ) query ( expected * ExpectedQuery ) ( driver . Rows , error ) {
defer time . Sleep ( expected . delay )
2015-07-17 12:14:30 +02:00
2017-02-07 12:20:08 +02:00
return expected . rows , nil
2015-07-17 12:14:30 +02:00
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectQuery ( sqlRegexStr string ) * ExpectedQuery {
2015-07-17 12:14:30 +02:00
e := & ExpectedQuery { }
2017-02-04 13:09:50 +02:00
sqlRegexStr = stripQuery ( sqlRegexStr )
2014-02-05 17:21:07 +03:00
e . sqlRegex = regexp . MustCompile ( sqlRegexStr )
2015-07-17 12:14:30 +02:00
c . expected = append ( c . expected , e )
return e
}
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectCommit ( ) * ExpectedCommit {
2015-07-17 12:14:30 +02:00
e := & ExpectedCommit { }
c . expected = append ( c . expected , e )
return e
}
2014-02-05 17:21:07 +03:00
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) ExpectRollback ( ) * ExpectedRollback {
2015-07-17 12:14:30 +02:00
e := & ExpectedRollback { }
c . expected = append ( c . expected , e )
return e
2014-02-05 17:21:07 +03:00
}
2015-07-17 12:14:30 +02:00
// Commit meets http://golang.org/pkg/database/sql/driver/#Tx
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) Commit ( ) error {
2015-08-26 13:28:01 +02:00
var expected * ExpectedCommit
2015-08-26 15:59:28 +02:00
var fulfilled int
2015-08-26 13:28:01 +02:00
var ok bool
for _ , next := range c . expected {
next . Lock ( )
if next . fulfilled ( ) {
next . Unlock ( )
2015-08-26 15:59:28 +02:00
fulfilled ++
2015-08-26 13:28:01 +02:00
continue
}
if expected , ok = next . ( * ExpectedCommit ) ; ok {
break
}
next . Unlock ( )
2015-08-28 10:06:14 +02:00
if c . ordered {
2015-08-26 15:59:28 +02:00
return fmt . Errorf ( "call to commit transaction, was not expected, next expectation is: %s" , next )
2015-08-26 13:28:01 +02:00
}
}
if expected == nil {
2015-08-26 15:59:28 +02:00
msg := "call to commit transaction was not expected"
if fulfilled == len ( c . expected ) {
msg = "all expectations were already fulfilled, " + msg
}
return fmt . Errorf ( msg )
2015-07-17 12:14:30 +02:00
}
2015-08-26 13:28:01 +02:00
expected . triggered = true
expected . Unlock ( )
return expected . err
2014-02-05 17:21:07 +03:00
}
2015-07-17 12:14:30 +02:00
// Rollback meets http://golang.org/pkg/database/sql/driver/#Tx
2015-08-28 10:06:14 +02:00
func ( c * sqlmock ) Rollback ( ) error {
2015-08-26 13:28:01 +02:00
var expected * ExpectedRollback
2015-08-26 15:59:28 +02:00
var fulfilled int
2015-08-26 13:28:01 +02:00
var ok bool
for _ , next := range c . expected {
next . Lock ( )
if next . fulfilled ( ) {
next . Unlock ( )
2015-08-26 15:59:28 +02:00
fulfilled ++
2015-08-26 13:28:01 +02:00
continue
}
2015-07-17 12:14:30 +02:00
2015-08-26 13:28:01 +02:00
if expected , ok = next . ( * ExpectedRollback ) ; ok {
break
}
2014-02-05 17:21:07 +03:00
2015-08-26 13:28:01 +02:00
next . Unlock ( )
2015-08-28 10:06:14 +02:00
if c . ordered {
2015-08-26 15:59:28 +02:00
return fmt . Errorf ( "call to rollback transaction, was not expected, next expectation is: %s" , next )
2015-07-17 12:14:30 +02:00
}
2014-02-05 17:21:07 +03:00
}
2015-08-26 13:28:01 +02:00
if expected == nil {
2015-08-26 15:59:28 +02:00
msg := "call to rollback transaction was not expected"
if fulfilled == len ( c . expected ) {
msg = "all expectations were already fulfilled, " + msg
}
return fmt . Errorf ( msg )
2015-08-26 13:28:01 +02:00
}
expected . triggered = true
expected . Unlock ( )
return expected . err
2014-02-05 17:21:07 +03:00
}