diff --git a/driver.go b/driver.go index 050aeef..54eb012 100644 --- a/driver.go +++ b/driver.go @@ -11,7 +11,7 @@ var pool *mockDriver func init() { pool = &mockDriver{ - conns: make(map[string]*Sqlmock), + conns: make(map[string]*sqlmock), } sql.Register("sqlmock", pool) } @@ -19,7 +19,7 @@ func init() { type mockDriver struct { sync.Mutex counter int - conns map[string]*Sqlmock + conns map[string]*sqlmock } func (d *mockDriver) Open(dsn string) (driver.Conn, error) { @@ -39,18 +39,18 @@ func (d *mockDriver) Open(dsn string) (driver.Conn, error) { // and a mock to manage expectations. // Pings db so that all expectations could be // asserted. -func New() (db *sql.DB, mock *Sqlmock, err error) { +func New() (db *sql.DB, mock Sqlmock, err error) { pool.Lock() dsn := fmt.Sprintf("sqlmock_db_%d", pool.counter) pool.counter++ - mock = &Sqlmock{dsn: dsn, drv: pool, MatchExpectationsInOrder: true} - pool.conns[dsn] = mock + smock := &sqlmock{dsn: dsn, drv: pool, ordered: true} + pool.conns[dsn] = smock pool.Unlock() db, err = sql.Open("sqlmock", dsn) if err != nil { return } - return db, mock, db.Ping() + return db, smock, db.Ping() } diff --git a/driver_test.go b/driver_test.go index 0554a22..aa60f12 100644 --- a/driver_test.go +++ b/driver_test.go @@ -25,14 +25,15 @@ func TestShouldOpenConnectionIssue15(t *testing.T) { t.Errorf("expected 1 connection in pool, but there is: %d", len(pool.conns)) } - if mock.opened != 1 { - t.Errorf("expected 1 connection on mock to be opened, but there is: %d", mock.opened) + smock, _ := mock.(*sqlmock) + if smock.opened != 1 { + t.Errorf("expected 1 connection on mock to be opened, but there is: %d", smock.opened) } // defer so the rows gets closed first defer func() { - if mock.opened != 0 { - t.Errorf("expected no connections on mock to be opened, but there is: %d", mock.opened) + if smock.opened != 0 { + t.Errorf("expected no connections on mock to be opened, but there is: %d", smock.opened) } }() @@ -49,8 +50,8 @@ func TestShouldOpenConnectionIssue15(t *testing.T) { } // now there should be two connections open - if mock.opened != 2 { - t.Errorf("expected 2 connection on mock to be opened, but there is: %d", mock.opened) + if smock.opened != 2 { + t.Errorf("expected 2 connection on mock to be opened, but there is: %d", smock.opened) } mock.ExpectClose() @@ -59,8 +60,8 @@ func TestShouldOpenConnectionIssue15(t *testing.T) { } // one is still reserved for rows - if mock.opened != 1 { - t.Errorf("expected 1 connection on mock to be still reserved for rows, but there is: %d", mock.opened) + if smock.opened != 1 { + t.Errorf("expected 1 connection on mock to be still reserved for rows, but there is: %d", smock.opened) } } diff --git a/expectations.go b/expectations.go index 55cef3c..36076e6 100644 --- a/expectations.go +++ b/expectations.go @@ -247,7 +247,7 @@ func (e *ExpectedExec) WillReturnResult(result driver.Result) *ExpectedExec { // Returned by *Sqlmock.ExpectPrepare. type ExpectedPrepare struct { commonExpectation - mock *Sqlmock + mock *sqlmock sqlRegex *regexp.Regexp statement driver.Stmt closeErr error diff --git a/result_test.go b/result_test.go index e02e5e3..08d47c9 100644 --- a/result_test.go +++ b/result_test.go @@ -6,7 +6,7 @@ import ( ) // used for examples -var mock = &Sqlmock{} +var mock = &sqlmock{} func ExampleNewErrorResult() { db, mock, _ := New() diff --git a/sqlmock.go b/sqlmock.go index 7ac5dbb..09a0bf9 100644 --- a/sqlmock.go +++ b/sqlmock.go @@ -18,11 +18,48 @@ import ( "regexp" ) -// Sqlmock type satisfies required sql.driver interfaces -// to simulate actual database and also serves to -// create expectations for any kind of database action -// in order to mock and test real database behavior. -type Sqlmock struct { +// 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 // MatchExpectationsInOrder gives an option whether to match all // expectations in the order they were set or not. @@ -30,29 +67,33 @@ type Sqlmock struct { // By default it is set to - true. But if you use goroutines // to parallelize your query executation, that option may // be handy. - MatchExpectationsInOrder bool + MatchExpectationsInOrder(bool) +} - dsn string - opened int - drv *mockDriver +type sqlmock struct { + ordered bool + dsn string + opened int + drv *mockDriver expected []expectation } -// ExpectClose queues an expectation for this database -// action to be triggered. the *ExpectedClose allows -// to mock database response -func (c *Sqlmock) ExpectClose() *ExpectedClose { +func (c *sqlmock) ExpectClose() *ExpectedClose { e := &ExpectedClose{} c.expected = append(c.expected, e) return e } +func (c *sqlmock) MatchExpectationsInOrder(b bool) { + c.ordered = b +} + // 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 -func (c *Sqlmock) Close() error { +func (c *sqlmock) Close() error { c.drv.Lock() defer c.drv.Unlock() @@ -77,7 +118,7 @@ func (c *Sqlmock) Close() error { } next.Unlock() - if c.MatchExpectationsInOrder { + if c.ordered { return fmt.Errorf("call to database Close, was not expected, next expectation is: %s", next) } } @@ -95,9 +136,7 @@ func (c *Sqlmock) Close() error { return expected.err } -// ExpectationsWereMet checks whether all queued expectations -// were met in order. If any of them was not met - an error is returned. -func (c *Sqlmock) ExpectationsWereMet() error { +func (c *sqlmock) ExpectationsWereMet() error { for _, e := range c.expected { if !e.fulfilled() { return fmt.Errorf("there is a remaining expectation which was not matched: %s", e) @@ -107,7 +146,7 @@ func (c *Sqlmock) ExpectationsWereMet() error { } // Begin meets http://golang.org/pkg/database/sql/driver/#Conn interface -func (c *Sqlmock) Begin() (driver.Tx, error) { +func (c *sqlmock) Begin() (driver.Tx, error) { var expected *ExpectedBegin var ok bool var fulfilled int @@ -124,7 +163,7 @@ func (c *Sqlmock) Begin() (driver.Tx, error) { } next.Unlock() - if c.MatchExpectationsInOrder { + if c.ordered { return nil, fmt.Errorf("call to database transaction Begin, was not expected, next expectation is: %s", next) } } @@ -141,16 +180,14 @@ func (c *Sqlmock) Begin() (driver.Tx, error) { return c, expected.err } -// ExpectBegin expects *sql.DB.Begin to be called. -// the *ExpectedBegin allows to mock database response -func (c *Sqlmock) ExpectBegin() *ExpectedBegin { +func (c *sqlmock) ExpectBegin() *ExpectedBegin { e := &ExpectedBegin{} c.expected = append(c.expected, e) return e } // Exec meets http://golang.org/pkg/database/sql/driver/#Execer -func (c *Sqlmock) Exec(query string, args []driver.Value) (res driver.Result, err error) { +func (c *sqlmock) Exec(query string, args []driver.Value) (res driver.Result, err error) { query = stripQuery(query) var expected *ExpectedExec var fulfilled int @@ -163,7 +200,7 @@ func (c *Sqlmock) Exec(query string, args []driver.Value) (res driver.Result, er continue } - if c.MatchExpectationsInOrder { + if c.ordered { if expected, ok = next.(*ExpectedExec); ok { break } @@ -218,10 +255,7 @@ func (c *Sqlmock) Exec(query string, args []driver.Value) (res driver.Result, er return expected.result, err } -// ExpectExec expects Exec() to be called with sql query -// which match sqlRegexStr given regexp. -// the *ExpectedExec allows to mock database response -func (c *Sqlmock) ExpectExec(sqlRegexStr string) *ExpectedExec { +func (c *sqlmock) ExpectExec(sqlRegexStr string) *ExpectedExec { e := &ExpectedExec{} e.sqlRegex = regexp.MustCompile(sqlRegexStr) c.expected = append(c.expected, e) @@ -229,7 +263,7 @@ func (c *Sqlmock) ExpectExec(sqlRegexStr string) *ExpectedExec { } // Prepare meets http://golang.org/pkg/database/sql/driver/#Conn interface -func (c *Sqlmock) Prepare(query string) (driver.Stmt, error) { +func (c *sqlmock) Prepare(query string) (driver.Stmt, error) { var expected *ExpectedPrepare var fulfilled int var ok bool @@ -246,7 +280,7 @@ func (c *Sqlmock) Prepare(query string) (driver.Stmt, error) { } next.Unlock() - if c.MatchExpectationsInOrder { + if c.ordered { return nil, fmt.Errorf("call to Prepare stetement with query '%s', was not expected, next expectation is: %s", query, next) } } @@ -265,19 +299,14 @@ func (c *Sqlmock) Prepare(query string) (driver.Stmt, error) { return &statement{c, query, expected.closeErr}, expected.err } -// 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 -func (c *Sqlmock) ExpectPrepare(sqlRegexStr string) *ExpectedPrepare { +func (c *sqlmock) ExpectPrepare(sqlRegexStr string) *ExpectedPrepare { e := &ExpectedPrepare{sqlRegex: regexp.MustCompile(sqlRegexStr), mock: c} c.expected = append(c.expected, e) return e } // Query meets http://golang.org/pkg/database/sql/driver/#Queryer -func (c *Sqlmock) Query(query string, args []driver.Value) (rw driver.Rows, err error) { +func (c *sqlmock) Query(query string, args []driver.Value) (rw driver.Rows, err error) { query = stripQuery(query) var expected *ExpectedQuery var fulfilled int @@ -290,7 +319,7 @@ func (c *Sqlmock) Query(query string, args []driver.Value) (rw driver.Rows, err continue } - if c.MatchExpectationsInOrder { + if c.ordered { if expected, ok = next.(*ExpectedQuery); ok { break } @@ -347,34 +376,27 @@ func (c *Sqlmock) Query(query string, args []driver.Value) (rw driver.Rows, err return expected.rows, err } -// ExpectQuery expects Query() or QueryRow() to be called with sql query -// which match sqlRegexStr given regexp. -// the *ExpectedQuery allows to mock database response. -func (c *Sqlmock) ExpectQuery(sqlRegexStr string) *ExpectedQuery { +func (c *sqlmock) ExpectQuery(sqlRegexStr string) *ExpectedQuery { e := &ExpectedQuery{} e.sqlRegex = regexp.MustCompile(sqlRegexStr) c.expected = append(c.expected, e) return e } -// ExpectCommit expects *sql.Tx.Commit to be called. -// the *ExpectedCommit allows to mock database response -func (c *Sqlmock) ExpectCommit() *ExpectedCommit { +func (c *sqlmock) ExpectCommit() *ExpectedCommit { e := &ExpectedCommit{} c.expected = append(c.expected, e) return e } -// ExpectRollback expects *sql.Tx.Rollback to be called. -// the *ExpectedRollback allows to mock database response -func (c *Sqlmock) ExpectRollback() *ExpectedRollback { +func (c *sqlmock) ExpectRollback() *ExpectedRollback { e := &ExpectedRollback{} c.expected = append(c.expected, e) return e } // Commit meets http://golang.org/pkg/database/sql/driver/#Tx -func (c *Sqlmock) Commit() error { +func (c *sqlmock) Commit() error { var expected *ExpectedCommit var fulfilled int var ok bool @@ -391,7 +413,7 @@ func (c *Sqlmock) Commit() error { } next.Unlock() - if c.MatchExpectationsInOrder { + if c.ordered { return fmt.Errorf("call to commit transaction, was not expected, next expectation is: %s", next) } } @@ -409,7 +431,7 @@ func (c *Sqlmock) Commit() error { } // Rollback meets http://golang.org/pkg/database/sql/driver/#Tx -func (c *Sqlmock) Rollback() error { +func (c *sqlmock) Rollback() error { var expected *ExpectedRollback var fulfilled int var ok bool @@ -426,7 +448,7 @@ func (c *Sqlmock) Rollback() error { } next.Unlock() - if c.MatchExpectationsInOrder { + if c.ordered { return fmt.Errorf("call to rollback transaction, was not expected, next expectation is: %s", next) } } diff --git a/sqlmock_test.go b/sqlmock_test.go index 1fb7af1..616b9e7 100644 --- a/sqlmock_test.go +++ b/sqlmock_test.go @@ -586,7 +586,7 @@ func TestGoroutineExecutionWithUnorderedExpectationMatching(t *testing.T) { defer db.Close() // note this line is important for unordered expectation matching - mock.MatchExpectationsInOrder = false + mock.MatchExpectationsInOrder(false) result := NewResult(1, 1) @@ -626,7 +626,7 @@ func ExampleSqlmock_goroutines() { defer db.Close() // note this line is important for unordered expectation matching - mock.MatchExpectationsInOrder = false + mock.MatchExpectationsInOrder(false) result := NewResult(1, 1) diff --git a/statement.go b/statement.go index f84b094..df73740 100644 --- a/statement.go +++ b/statement.go @@ -5,7 +5,7 @@ import ( ) type statement struct { - conn *Sqlmock + conn *sqlmock query string err error }