From 081a694b0af16f86d406ac2832433a98a6fa625a Mon Sep 17 00:00:00 2001 From: gedi Date: Thu, 10 Sep 2015 21:31:35 +0300 Subject: [PATCH] closes #24 --- .travis.yml | 6 ++++-- driver.go | 34 ++++++++++++++++++++++++++++------ driver_test.go | 33 +++++++++++++++++++++++++++++++++ sqlmock.go | 9 +++++++++ 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8d7ddf0..72323d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,11 @@ go: - 1.2 - 1.3 - 1.4 + - 1.5 - release - tip script: - - go test -v ./... - - go test -race ./... + - go get -t + - go test -v + - go test -race diff --git a/driver.go b/driver.go index 54eb012..2a480fe 100644 --- a/driver.go +++ b/driver.go @@ -39,7 +39,7 @@ 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() (*sql.DB, Sqlmock, error) { pool.Lock() dsn := fmt.Sprintf("sqlmock_db_%d", pool.counter) pool.counter++ @@ -48,9 +48,31 @@ func New() (db *sql.DB, mock Sqlmock, err error) { pool.conns[dsn] = smock pool.Unlock() - db, err = sql.Open("sqlmock", dsn) - if err != nil { - return - } - return db, smock, db.Ping() + return smock.open() +} + +// NewWithDSN creates sqlmock database connection +// with a specific DSN and a mock to manage expectations. +// Pings db so that all expectations could be asserted. +// +// This method is introduced because of sql abstraction +// libraries, which do not provide a way to initialize +// with sql.DB instance. For example GORM library. +// +// Note, it will error if attempted to create with an +// already used dsn +// +// It is not recommended to use this method, unless you +// really need it and there is no other way around. +func NewWithDSN(dsn string) (*sql.DB, Sqlmock, error) { + pool.Lock() + if _, ok := pool.conns[dsn]; ok { + pool.Unlock() + return nil, nil, fmt.Errorf("cannot create a new mock database with the same dsn: %s", dsn) + } + smock := &sqlmock{dsn: dsn, drv: pool, ordered: true} + pool.conns[dsn] = smock + pool.Unlock() + + return smock.open() } diff --git a/driver_test.go b/driver_test.go index aa60f12..407b619 100644 --- a/driver_test.go +++ b/driver_test.go @@ -3,8 +3,14 @@ package sqlmock import ( "fmt" "testing" + + "github.com/jinzhu/gorm" ) +type void struct{} + +func (void) Print(...interface{}) {} + func ExampleNew() { db, mock, err := New() if err != nil { @@ -16,6 +22,33 @@ func ExampleNew() { mock.ExpectBegin().WillReturnError(fmt.Errorf("an error will occur on db.Begin() call")) } +func ExampleNewWithDSN() { + _, mock, err := NewWithDSN("mydsn for gorm") + if err != nil { + fmt.Println("expected no error, but got:", err) + return + } + // use the same dsn to initialize a connection + db, err := gorm.Open("sqlmock", "mydsn for gorm") + if err != nil { + fmt.Println("expected no error from gorm, but got:", err) + return + } + defer db.Close() + db.SetLogger(void{}) // sad - nil panics + + // now we can expect operations performed on db + mock.ExpectBegin().WillReturnError(fmt.Errorf("an error will occur on db.Begin() call")) + + txconn := db.Begin() + for _, err := range txconn.GetErrors() { + fmt.Println(err.Error()) + } + + // Output: `sqlmock` is not officially supported, running under compatibility mode. + // an error will occur on db.Begin() call +} + func TestShouldOpenConnectionIssue15(t *testing.T) { db, mock, err := New() if err != nil { diff --git a/sqlmock.go b/sqlmock.go index 09a0bf9..1fbe8b4 100644 --- a/sqlmock.go +++ b/sqlmock.go @@ -12,6 +12,7 @@ are also supported. package sqlmock import ( + "database/sql" "database/sql/driver" "fmt" "reflect" @@ -79,6 +80,14 @@ type sqlmock struct { expected []expectation } +func (s *sqlmock) open() (*sql.DB, Sqlmock, error) { + db, err := sql.Open("sqlmock", s.dsn) + if err != nil { + return db, s, err + } + return db, s, db.Ping() +} + func (c *sqlmock) ExpectClose() *ExpectedClose { e := &ExpectedClose{} c.expected = append(c.expected, e)