2014-02-07 17:52:26 +03:00
|
|
|
[![Build Status](https://travis-ci.org/DATA-DOG/go-sqlmock.png)](https://travis-ci.org/DATA-DOG/go-sqlmock)
|
2014-02-08 18:51:58 +03:00
|
|
|
[![GoDoc](https://godoc.org/github.com/DATA-DOG/go-sqlmock?status.png)](https://godoc.org/github.com/DATA-DOG/go-sqlmock)
|
2014-02-07 17:52:26 +03:00
|
|
|
|
2014-02-06 18:03:46 +03:00
|
|
|
# Sql driver mock for Golang
|
2014-02-05 17:21:07 +03:00
|
|
|
|
2014-02-06 18:03:46 +03:00
|
|
|
This is a **mock** driver as **database/sql/driver** which is very flexible and pragmatic to
|
|
|
|
manage and mock expected queries. All the expectations should be met and all queries and actions
|
2015-07-17 12:14:30 +02:00
|
|
|
triggered should be mocked in order to pass a test. The package has no 3rd party dependencies.
|
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
**NOTE:** regarding major issues #20 and #9 the **api** has changed to support concurrency and more than
|
2015-07-17 12:14:30 +02:00
|
|
|
one database connection.
|
|
|
|
|
|
|
|
If you need an old version, checkout **go-sqlmock** at gopkg.in:
|
|
|
|
|
|
|
|
go get gopkg.in/DATA-DOG/go-sqlmock.v0
|
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
Otherwise use the **v1** branch from master which should be stable afterwards, because all the issues which
|
|
|
|
were known will be fixed in this version.
|
2014-02-05 17:21:07 +03:00
|
|
|
|
2014-02-06 18:03:46 +03:00
|
|
|
## Install
|
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
go get gopkg.in/DATA-DOG/go-sqlmock.v1
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-07-17 12:14:30 +02:00
|
|
|
Or take an older version:
|
|
|
|
|
|
|
|
go get gopkg.in/DATA-DOG/go-sqlmock.v0
|
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
## Documentation and Examples
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
Visit [godoc](http://godoc.org/github.com/DATA-DOG/go-sqlmock) for general examples and public api reference.
|
|
|
|
See **.travis.yml** for supported **go** versions.
|
|
|
|
Different use case, is to functionally test with a real database - [go-txdb](https://github.com/DATA-DOG/go-txdb)
|
|
|
|
all database related actions are isolated within a single transaction so the database can remain in the same state.
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
See implementation examples:
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
- [blog API server](https://github.com/DATA-DOG/go-sqlmock/tree/master/examples/blog)
|
|
|
|
- [the same orders example](https://github.com/DATA-DOG/go-sqlmock/tree/master/examples/orders)
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
### Something you may want to test
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
``` go
|
|
|
|
package main
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
import "database/sql"
|
|
|
|
|
|
|
|
func recordStats(db *sql.DB, userID, productID int64) (err error) {
|
|
|
|
tx, err := db.Begin()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
switch err {
|
|
|
|
case nil:
|
|
|
|
err = tx.Commit()
|
|
|
|
default:
|
|
|
|
tx.Rollback()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return
|
2014-02-06 18:03:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2015-08-27 09:34:01 +02:00
|
|
|
// @NOTE: the real connection is not required for tests
|
|
|
|
db, err := sql.Open("mysql", "root@/blog")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2014-02-06 18:03:46 +03:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
### Tests with sqlmock
|
2014-02-06 18:03:46 +03:00
|
|
|
|
|
|
|
``` go
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-08-27 09:34:01 +02:00
|
|
|
"fmt"
|
|
|
|
"testing"
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
|
|
)
|
2014-02-06 18:03:46 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
// a successful case
|
|
|
|
func TestShouldUpdateStats(t *testing.T) {
|
|
|
|
db, mock, err := sqlmock.New()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
mock.ExpectBegin()
|
|
|
|
mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
mock.ExpectCommit()
|
|
|
|
|
|
|
|
// now we execute our method
|
|
|
|
if err = recordStats(db, 2, 3); err != nil {
|
|
|
|
t.Errorf("error was not expected while updating stats: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// we make sure that all expectations were met
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
|
|
t.Errorf("there were unfulfilled expections: %s", err)
|
|
|
|
}
|
2014-02-06 18:03:46 +03:00
|
|
|
}
|
2014-02-14 01:14:32 +03:00
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
// a failing test case
|
|
|
|
func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {
|
|
|
|
db, mock, err := sqlmock.New()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
mock.ExpectBegin()
|
|
|
|
mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
mock.ExpectExec("INSERT INTO product_viewers").
|
|
|
|
WithArgs(2, 3).
|
|
|
|
WillReturnError(fmt.Errorf("some error"))
|
|
|
|
mock.ExpectRollback()
|
|
|
|
|
|
|
|
// now we execute our method
|
|
|
|
if err = recordStats(db, 2, 3); err == nil {
|
|
|
|
t.Errorf("was expecting an error, but there was none")
|
|
|
|
}
|
|
|
|
|
|
|
|
// we make sure that all expectations were met
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
|
|
t.Errorf("there were unfulfilled expections: %s", err)
|
|
|
|
}
|
2014-02-14 01:14:32 +03:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2014-02-06 18:03:46 +03:00
|
|
|
## Run tests
|
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
go test -race
|
2014-02-07 11:44:41 +03:00
|
|
|
|
2014-02-14 01:14:32 +03:00
|
|
|
## Changes
|
|
|
|
|
2015-08-27 09:34:01 +02:00
|
|
|
- **2015-08-27** - **v1** api change, concurrency support, all known issues fixed.
|
2014-08-16 12:48:11 +03:00
|
|
|
- **2014-08-16** instead of **panic** during reflect type mismatch when comparing query arguments - now return error
|
2014-08-14 20:35:18 +03:00
|
|
|
- **2014-08-14** added **sqlmock.NewErrorResult** which gives an option to return driver.Result with errors for
|
|
|
|
interface methods, see [issue](https://github.com/DATA-DOG/go-sqlmock/issues/5)
|
2014-05-29 17:11:13 +03:00
|
|
|
- **2014-05-29** allow to match arguments in more sophisticated ways, by providing an **sqlmock.Argument** interface
|
2014-04-21 18:21:28 +03:00
|
|
|
- **2014-04-21** introduce **sqlmock.New()** to open a mock database connection for tests. This method
|
|
|
|
calls sql.DB.Ping to ensure that connection is open, see [issue](https://github.com/DATA-DOG/go-sqlmock/issues/4).
|
|
|
|
This way on Close it will surely assert if all expectations are met, even if database was not triggered at all.
|
|
|
|
The old way is still available, but it is advisable to call db.Ping manually before asserting with db.Close.
|
2014-02-14 01:14:32 +03:00
|
|
|
- **2014-02-14** RowsFromCSVString is now a part of Rows interface named as FromCSVString.
|
|
|
|
It has changed to allow more ways to construct rows and to easily extend this API in future.
|
|
|
|
See [issue 1](https://github.com/DATA-DOG/go-sqlmock/issues/1)
|
|
|
|
**RowsFromCSVString** is deprecated and will be removed in future
|
|
|
|
|
2014-02-06 18:03:46 +03:00
|
|
|
## Contributions
|
|
|
|
|
2014-02-07 09:58:27 +03:00
|
|
|
Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) -
|
|
|
|
please open an issue before, to discuss whether these changes can be accepted. All backward incompatible changes are
|
|
|
|
and will be treated cautiously
|
2014-02-06 18:03:46 +03:00
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
The [three clause BSD license](http://en.wikipedia.org/wiki/BSD_licenses)
|
2014-02-05 17:21:07 +03:00
|
|
|
|