Code Monkey home page Code Monkey logo

go-sqlmock's Introduction

Build Status GoDoc Go Report Card codecov.io

Sql driver mock for Golang

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.

  • this library is now complete and stable. (you may not find new changes for this reason)
  • supports concurrency and multiple connections.
  • supports go1.8 Context related feature mocking and Named sql parameters.
  • does not require any modifications to your source code.
  • the driver allows to mock any sql driver method behavior.
  • has strict by default expectation order matching.
  • has no third party dependencies.

NOTE: in v1.2.0 sqlmock.Rows has changed to struct from interface, if you were using any type references to that interface, you will need to switch it to a pointer struct type. Also, sqlmock.Rows were used to implement driver.Rows interface, which was not required or useful for mocking and was removed. Hope it will not cause issues.

Looking for maintainers

I do not have much spare time for this library and willing to transfer the repository ownership to person or an organization motivated to maintain it. Open up a conversation if you are interested. See #230.

Install

go get github.com/DATA-DOG/go-sqlmock

Documentation and Examples

Visit godoc 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 all database related actions are isolated within a single transaction so the database can remain in the same state.

See implementation examples:

Something you may want to test, assuming you use the go-mysql-driver

package main

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

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
}

func main() {
	// @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)
	}
}

Tests with sqlmock

package main

import (
	"fmt"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
)

// 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 expectations: %s", err)
	}
}

// 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 expectations: %s", err)
	}
}

Customize SQL query matching

There were plenty of requests from users regarding SQL query string validation or different matching option. We have now implemented the QueryMatcher interface, which can be passed through an option when calling sqlmock.New or sqlmock.NewWithDSN.

This now allows to include some library, which would allow for example to parse and validate mysql SQL AST. And create a custom QueryMatcher in order to validate SQL in sophisticated ways.

By default, sqlmock is preserving backward compatibility and default query matcher is sqlmock.QueryMatcherRegexp which uses expected SQL string as a regular expression to match incoming query string. There is an equality matcher: QueryMatcherEqual which will do a full case sensitive match.

In order to customize the QueryMatcher, use the following:

	db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))

The query matcher can be fully customized based on user needs. sqlmock will not provide a standard sql parsing matchers, since various drivers may not follow the same SQL standard.

Matching arguments like time.Time

There may be arguments which are of struct type and cannot be compared easily by value like time.Time. In this case sqlmock provides an Argument interface which can be used in more sophisticated matching. Here is a simple example of time argument matching:

type AnyTime struct{}

// Match satisfies sqlmock.Argument interface
func (a AnyTime) Match(v driver.Value) bool {
	_, ok := v.(time.Time)
	return ok
}

func TestAnyTimeArgument(t *testing.T) {
	t.Parallel()
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	mock.ExpectExec("INSERT INTO users").
		WithArgs("john", AnyTime{}).
		WillReturnResult(sqlmock.NewResult(1, 1))

	_, err = db.Exec("INSERT INTO users(name, created_at) VALUES (?, ?)", "john", time.Now())
	if err != nil {
		t.Errorf("error '%s' was not expected, while inserting a row", err)
	}

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("there were unfulfilled expectations: %s", err)
	}
}

It only asserts that argument is of time.Time type.

Run tests

go test -race

Change Log

  • 2019-04-06 - added functionality to mock a sql MetaData request
  • 2019-02-13 - added go.mod removed the references and suggestions using gopkg.in.
  • 2018-12-11 - added expectation of Rows to be closed, while mocking expected query.
  • 2018-12-11 - introduced an option to provide QueryMatcher in order to customize SQL query matching.
  • 2017-09-01 - it is now possible to expect that prepared statement will be closed, using ExpectedPrepare.WillBeClosed.
  • 2017-02-09 - implemented support for go1.8 features. Rows interface was changed to struct but contains all methods as before and should maintain backwards compatibility. ExpectedQuery.WillReturnRows may now accept multiple row sets.
  • 2016-11-02 - db.Prepare() was not validating expected prepare SQL query. It should still be validated even if Exec or Query is not executed on that prepared statement.
  • 2016-02-23 - added sqlmock.AnyArg() function to provide any kind of argument matcher.
  • 2016-02-23 - convert expected arguments to driver.Value as natural driver does, the change may affect time.Time comparison and will be stricter. See issue.
  • 2015-08-27 - v1 api change, concurrency support, all known issues fixed.
  • 2014-08-16 instead of panic during reflect type mismatch when comparing query arguments - now return error
  • 2014-08-14 added sqlmock.NewErrorResult which gives an option to return driver.Result with errors for interface methods, see issue
  • 2014-05-29 allow to match arguments in more sophisticated ways, by providing an sqlmock.Argument interface
  • 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. 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 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 RowsFromCSVString is deprecated and will be removed in future

Contributions

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

License

The three clause BSD license

go-sqlmock's People

Contributors

aleksi avatar asahasrabuddhe avatar ashhadsheikh avatar co60ca avatar col3name avatar dackroyd avatar dolmen avatar fatelei avatar filikos avatar ghvstcode avatar gliptak avatar gold-kou avatar hebo avatar ivogoman avatar jekiapp avatar jessieamorris avatar krevels avatar l3pp4rd avatar midakaloom avatar mikemacd avatar mrroman avatar nineinchnick avatar omnicolor avatar pawan-bishnoi avatar sekky0905 avatar sfc-gh-tvidyasankar avatar skriptble avatar sleeping-barber avatar thevisus avatar whiteheadrj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-sqlmock's Issues

Intermittent failure on mock.ExpectQuery("your regex").WillReturnRows(your_rows)

I have a test that mocks 2 subsequent DB queries. After an upgrade to 1.8.1 Go version the second mock fails to return the data every other time. On 1.7.4_2 it was working consistently with no issues.

Here are my 2 mocks:

		mock.ExpectQuery("^SELECT (.+) COALESCE(.+)$").WillReturnRows(rows)
		mock.ExpectQuery("^SELECT count(.+)$").WillReturnRows(rowsForTotal)

ExpectRollback doesn't trigger

I'm not sure if I'm doing this right but ExpectRollback doesn't trigger in this case :

func TestRollback(t *testing.T) {
    db, err := sqlmock.New()
    if err != nil {
        t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
    }

    sqlmock.ExpectBegin()
    sqlmock.ExpectRollback()

    _, errBegin := db.Begin()
    if errBegin != nil {
        t.Fatal(errBegin)
    }

    err = db.Close()
    if err == nil {
        t.Fail()
    }
}

go-sqlmock, ExpectQuery not working for gorp SelectOne?

Hi, as stated in the title, I am having an issue testing an api endpoint with ExpectQuery. I am using the 'Gin' framework.

This is not working as expected:

mock.ExpectQuery("^SELECT (.+) FROM user where id=? LIMIT 1$").WithArgs(1).WillReturnRows(rows)

it's failing and returning:

query 'SELECT * FROM user WHERE id=? LIMIT 1', does not match regex [^SELECT (.+) FROM user where id=? LIMIT 1$]

I have created a gist here which reproduces the issue. https://gist.github.com/dvwright/490ed14a72ae7dab818026b1081f689c

The 'TestGetUsers' test passes, the 'TestGetSpecificUser' test does not pass.

I am new to golang so please feel free to point out user error, or another way to accomplish my goal.

Thanks

Results: A query should be allowed to return 0 rows

For instance, maybe you are trying to find a user based on their email address, if the query comes back with 0 rows in real life, you know that the user doesn't exist. There should be a way to return 0 rows without throwing an error.

Checking correctness of query sintax

suppose I've made mistake on writing the query on both of the mock and the real query for insert data. For example :
mock.ExpectExec("INSER INTO table").WithArgs("2017-12-21", 1234).WillReturnResult(sqlmock.NewResult(1, 1))

and the query sintax :

buff := bytes.NewBufferString(`
        INSER INTO table(
            date,
            shop_id
        ) VALUES ($1, $2)
    `)
query := txShop.Rebind(buff.String())
_, err = txShop.Exec(query, Date, shopIdInt)

Notice that I wrote the wrong sintax on INSER it should be INSERT. is there any way to add new checking algorithm of sintax query?

Unable to mock results with datetime columns

Hey,

How can I mock columns that are stored as datetimes? It seems that RowsFromCSVString sets driver.Values that are always []byte, and Scan is unable to parse time.time objects from them:

Scan error on column index 4: unsupported driver -> Scan pair: []uint8 -> *time.Time

I'd guess this would work if either RowsFromCSVString was able to directly create time.time objects when it encounters them or there was another way to assemble sqlmock.rows objects.

Example:

sqlmock.ExpectQuery("SELECT now()").
    WillReturnRows(sqlmock.RowsFromCSVString([]string{"now"}, "2014-02-12 16:04:15.879588-08"))

# sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time

False positive in ExpectationsWereMet

We have noticed a false positive problem when asking if the ExpectationsWereMet with MatchExpectationsInOrder = true, when expecting Exec calls against the database.

The problem is not in the order matching, but that outstanding expectations have not been satisfied.

Here is an example. Both the tests in sqlexec_test.go should be failing because RunExecs second call to Exec is not expected and as such one of our expected Exec calls has not been satisfied. The first test, where MatchExpectationsInOrder = true, is passing - it should not.

Tested on version 3769fed


We think the culprit is that the expectation matched in Exec has triggered = true before the query is matched. This currently happens on line 234. Moving this to just above line 251 resolves the issue.

Impossible to mock errors return from rows.Scan()

Hi,

I've been using a lot of go-sqlmock and I'm really liking it. However, there doesn't seem to be way to mock the error returned from rows.Scan() when called on an object returned from database.Query (a rows object). There is an example of how to use RowError to check the error returned from rows.Err(), but this is not the same as the error returned from rows.Scan().

Here I've extended the RowError example from godoc to illustrate my issue:

db, mock, err := New()
if err != nil {
    fmt.Println("failed to open sqlmock database:", err)
}
defer db.Close()

rows := NewRows([]string{"id", "title"}).
    AddRow(0, "one").
    AddRow(1, "two").
    RowError(1, fmt.Errorf("row error"))
mock.ExpectQuery("SELECT").WillReturnRows(rows)

rs, _ := db.Query("SELECT")
defer rs.Close()

for rs.Next() {
    var id int
    var title string
    // -------------------------------------------------
    // How do we mock this error?
    err := rs.Scan(&id, &title)
    if err != nil {
        fmt.Println("got scan error:", err)
        break
    }
    // -------------------------------------------------
    fmt.Println("scanned id:", id, "and title:", title)
}

if rs.Err() != nil {
    fmt.Println("got rows error:", rs.Err())
}

Race on ExpectQuery and Query

Hi,

We have an application that we're using with go-sqlmock, we're receiving a race warning using release d4cd2ca.

Output from go test -race

WARNING: DATA RACE
Write at 0x00c420066e68 by goroutine 32:
  domain.example.com/group/project/vendor/gopkg.in/DATA-DOG/go-sqlmock%2ev1.(*sqlmock).ExpectQuery()
      /u01/dev/go/src/domain.example.com/group/project/vendor/gopkg.in/DATA-DOG/go-sqlmock.v1/sqlmock.go:373 +0x19e
  domain.example.com/group/project.TestprojectManager()
      /u01/dev/go/src/domain.example.com/group/project/project_test.go:40 +0xba8
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:610 +0xc9

Previous read at 0x00c420066e68 by goroutine 34:
  domain.example.com/group/project/vendor/gopkg.in/DATA-DOG/go-sqlmock%2ev1.(*sqlmock).Query()
      /u01/dev/go/src/domain.example.com/group/project/vendor/gopkg.in/DATA-DOG/go-sqlmock.v1/sqlmock.go:341 +0xaa5
  database/sql.(*DB).queryConn()
      /usr/local/go/src/database/sql/sql.go:1092 +0x68b
  database/sql.(*DB).query()
      /usr/local/go/src/database/sql/sql.go:1079 +0x188
  database/sql.(*DB).Query()
      /usr/local/go/src/database/sql/sql.go:1062 +0xad
  database/sql.(*DB).QueryRow()
      /usr/local/go/src/database/sql/sql.go:1143 +0x80
  domain.example.com/group/project.(*channel).poll()
      /u01/dev/go/src/domain.example.com/group/project/channel.go:84 +0x2e8
  domain.example.com/group/project.(*channel).poller()
      /u01/dev/go/src/domain.example.com/group/project/channel.go:58 +0x9e
      
Goroutine 32 (running) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:646 +0x52f
  testing.RunTests.func1()
      /usr/local/go/src/testing/testing.go:793 +0xb9
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:610 +0xc9
  testing.RunTests()
      /usr/local/go/src/testing/testing.go:799 +0x4ba
  testing.(*M).Run()
      /usr/local/go/src/testing/testing.go:743 +0x12f
  main.main()
      domain.example.com/group/project/_test/_testmain.go:78 +0x1b8

Goroutine 34 (running) created at:
  domain.example.com/group/project.newChannel()
      /u01/dev/go/src/domain.example.com/group/project/channel.go:40 +0x1e0
  domain.example.com/group/project.(*projectManager).add()
      /u01/dev/go/src/domain.example.com/group/project/project.go:37 +0xca
  domain.example.com/group/project.(*projectManager).pollDB()
      /u01/dev/go/src/domain.example.com/group/project/project.go:87 +0x684
  domain.example.com/group/project.NewprojectManager()
      /u01/dev/go/src/domain.example.com/group/project/project.go:28 +0x11f
  domain.example.com/group/project.TestprojectManager()
      /u01/dev/go/src/domain.example.com/group/project/project_test.go:26 +0x70c
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:610 +0xc9

Essentially:

  • The *sql.DB object is passed to a background thread (Goroutine 34) which polls continuously and rapidly (this is the Query call in the previous read section of the above race).
  • On line 40 of project_test.go (Goroutine 32), we call mock.ExpectQuery(....)

I'm raising this issue as the README states supports concurrency and multiple connections and I can't easily work around this (having all Expect* calls before Goroutine 34 would be executed) without greatly limiting the effectiveness of the test.

Any thoughts, it appears the c.expected may need to be guarded as well?

Can `MatchExpectationsInOrder` be modified after some expectations have been already set?

How does this method affect expectation matching? There could be two ways it could work:

  • Allow random order on all expectations set before or after calling this method.
  • Allow random order on expectations set after calling this method while expectations set before calling this method are expected to be in order.

Which of the above (or some other logic) is the current functionality? The documentation seems to have skipped over this.

Convert expected arguments to driver values

Currently if expected argument is not converted to a supported driver.Value. That means the matching might be not exact or in some cases misleading and requires an user to expect already converted arguments.
Sqlmock should perform the same conversions as sql library for all expected arguments. That will be backward compatible and acting more as expected.

The changes may affect time.Time comparison, since it will be more strict.
The good outcome is that an error message for argument comparison will be more precise and will show exactly which argument did not match.

Package not usable with parallel tests

There is currently a data race when using go-sqlmock in two tests in parallel due to the way the API is currently designed. There is only ever one "mock" database and all operations are done on this. This means two tests cannot use this package in parallel. This is evidenced from the following data race in a package I am developing. I'm going to take a stab and changing the API to accommodate this.

==================
WARNING: DATA RACE
Read by goroutine 10:
  github.com/DATA-DOG/go-sqlmock.ExpectQuery()
      /Users/jnwhiteh/go/src/github.com/DATA-DOG/go-sqlmock/sqlmock.go:155 +0x138
  github.com/jnwhiteh/migration.setupVersioned()
      /Users/jnwhiteh/go/src/github.com/jnwhiteh/migration/migration_test.go:35 +0x22a
  github.com/jnwhiteh/migration.TestDowngradesUnsupported()
      /Users/jnwhiteh/go/src/github.com/jnwhiteh/migration/migration_test.go:61 +0x73
  testing.tRunner()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:422 +0x10f

Previous write by goroutine 8:
  github.com/DATA-DOG/go-sqlmock.(*conn).Close()
      /Users/jnwhiteh/go/src/github.com/DATA-DOG/go-sqlmock/connection.go:24 +0x266
  database/sql.(*driverConn).finalClose()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/database/sql/sql.go:310 +0x175
  database/sql.finalCloser.(database/sql.finalClose)ยทfm()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/database/sql/sql.go:398 +0x57
  database/sql.(*DB).Close()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/database/sql/sql.go:487 +0x6fa
  github.com/jnwhiteh/migration.TestFailingToGetCurrentVersion()
      /Users/jnwhiteh/go/src/github.com/jnwhiteh/migration/migration_test.go:54 +0x331
  testing.tRunner()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:422 +0x10f

Goroutine 10 (running) created at:
  testing.RunTests()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:504 +0xb46
  testing.Main()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:435 +0xa2
  main.main()
      github.com/jnwhiteh/migration/_test/_testmain.go:61 +0xdc

Goroutine 8 (finished) created at:
  testing.RunTests()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:504 +0xb46
  testing.Main()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:435 +0xa2
  main.main()
      github.com/jnwhiteh/migration/_test/_testmain.go:61 +0xdc
==================
==================
WARNING: DATA RACE
Write by goroutine 10:
  github.com/DATA-DOG/go-sqlmock.ExpectQuery()
      /Users/jnwhiteh/go/src/github.com/DATA-DOG/go-sqlmock/sqlmock.go:156 +0x309
  github.com/jnwhiteh/migration.setupVersioned()
      /Users/jnwhiteh/go/src/github.com/jnwhiteh/migration/migration_test.go:35 +0x22a
  github.com/jnwhiteh/migration.TestDowngradesUnsupported()
      /Users/jnwhiteh/go/src/github.com/jnwhiteh/migration/migration_test.go:61 +0x73
  testing.tRunner()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:422 +0x10f

Previous write by goroutine 8:
  github.com/DATA-DOG/go-sqlmock.(*conn).Close()
      /Users/jnwhiteh/go/src/github.com/DATA-DOG/go-sqlmock/connection.go:25 +0x2d0
  database/sql.(*driverConn).finalClose()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/database/sql/sql.go:310 +0x175
  database/sql.finalCloser.(database/sql.finalClose)ยทfm()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/database/sql/sql.go:398 +0x57
  database/sql.(*DB).Close()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/database/sql/sql.go:487 +0x6fa
  github.com/jnwhiteh/migration.TestFailingToGetCurrentVersion()
      /Users/jnwhiteh/go/src/github.com/jnwhiteh/migration/migration_test.go:54 +0x331
  testing.tRunner()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:422 +0x10f

Goroutine 10 (running) created at:
  testing.RunTests()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:504 +0xb46
  testing.Main()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:435 +0xa2
  main.main()
      github.com/jnwhiteh/migration/_test/_testmain.go:61 +0xdc

Goroutine 8 (finished) created at:
  testing.RunTests()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:504 +0xb46
  testing.Main()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/testing/testing.go:435 +0xa2
  main.main()
      github.com/jnwhiteh/migration/_test/_testmain.go:61 +0xdc
==================
PASS
Found 2 data race(s)
exit status 66
FAIL    github.com/jnwhiteh/migration   1.105s

Allow expectations to be reused

I think it would be useful to allow the option of expectations to be reused by adding a flag similar to MatchExpectationsInOrder defaulting to false. When set it would add a bypass to the continue in the if next.fulfilled() { ... } blocks.

ExpectePrepare does not work as documented

Hi everyone!
First, thanks for the excellent work with sqlmock.

I'm writing some test code with the help of go sqlmock.

The following code does not validates if the prepared statement has the correct query.

// main.go
package main

import "database/sql"

func testFunction(db *sql.DB) {
	db.Prepare("SELECT")
}

func main() {}
// main_test.go
package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
	sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
)

func TestPrepare(t *testing.T) {
	db, mock, mErr := sqlmock.New()
	assert.NoError(t, mErr)
	defer db.Close()

	mock.ExpectPrepare("^BADSELECT$")

	testFunction(db)

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("there were unfulfilled expections: %s", err)
	}

}

As far as i know, the test should fail, but it does't. What i'm i doing wrong?
I'm using GO 1.7/macOS 10.12.1.

Thanks

Include Any{Type} to package.

Hey,

I was wondering if it would be beneficial to add AnyTime to the library so that I don't need to define it in my test code.

I would also wonder if a AnyString would be useful. I had to create something like that to handle mocking the creation of a User model with a password that need to be encrypted via bcrypt before storing into the database. AnyString is useful because bcrypt doesn't always output the same thing every time and makes matching troublesome.

Thanks again for this library!

argMatches doesn't handle []byte properly

Line 352 of expectations.go causes a panic when the driver.Value is a slice of bytes because it is an uncomparable type:

panic: runtime error: comparing uncomparable type []uint8

This panic isn't recovered because it is called directly from sqlmock.Exec. A solution would be to use reflect.DeepEqual instead of using a != check. Another would be to check if either the dargs or the args[k] are slices of bytes and then user bytes.Equal, if not then default to the != check.

ExpectQuery putting mock select statement inside brackets

I have the following select statement in a GetLinks function:

r.DB.Query("SELECT * FROM links ORDER BY short_url ASC")

And am testing it as follows:

func GetLinksTest(mock sqlmock.Sqlmock, r *repo.Repo, t *testing.T, err error) {
    mock.ExpectQuery("SELECT * FROM links ORDER BY short_url ASC").WillReturnRows(link_rows)

    if _, err = r.GetLinks(); err != nil {
        t.Errorf("error was not expected while selecting from links: %s", err)
    }
}

The error that is returned is:
error was not expected while selecting from links: query 'SELECT * FROM links ORDER BY short_url ASC', does not match regex [SELECT * FROM links ORDER BY short_url ASC]

*Note the brackets added around the mock select statement

The most peculiar part of the whole thing is that if I add brackets to my original select statement, they still do not match.

r.DB.Query("[SELECT * FROM links ORDER BY short_url ASC]")

returns

error was not expected while selecting from links: query '[SELECT * FROM links ORDER BY short_url ASC]', does not match regex [SELECT * FROM links ORDER BY short_url ASC]

BUT, if I add brackets to the mock query and not the original query, the test passes!

row, err := r.DB.Query("SELECT * FROM links ORDER BY short_url ASC")
mock.ExpectQuery("[SELECT * FROM links ORDER BY short_url ASC]").WillReturnRows(link_rows)

How is it that brackets are being added to the mock query, and then magically being taken away by adding brackets?

go get path

Seems like the install instructions need to be updated:

go get github.com/DATA-DOG/go-sqlmock

Unable to test Prepare()

I'm unable to test the Prepare statement returning an error.

Generating a pull request for an addition of ExpectPrepare(). Made backwards compatible by having Prepare() ignore other expectations when this one not set.

Match a multi argument query against an expectation with an unique argument(s)

I'm hitting an issue where I want to mock a query but I don't know about (or in my particular case; don't care about) some of the arguments that the query was made with. I Also don't know what order the queries are made in

consider this somewhat convoluted example:

https://gist.github.com/nicwest/f902199d47f5c4a7902a

I would like to do something along the lines of

    mock.ExpectQuery("SELECT word FROM things").WithUniqueArgs("Fred").WillReturnRows(
        sqlmock.NewRows(columns).AddRow("Fish"),
    )

    mock.ExpectQuery("SELECT word FROM things").WithUniqueArgs("Bat").WillReturnRows(
        sqlmock.NewRows(columns).AddRow("Bat"),
    )

    mock.ExpectQuery("SELECT word FROM things").WithUniqueArgs("Harry").WillReturnRows(
        sqlmock.NewRows(columns).AddRow("Horse"),
    )

Any suggestions?

Is there a way to use v1 in combination with GORM?

The previous version allowed to simply pass the mock driver name to the GORM initialiser. Since the new version doesn't expose any interface to set the sql.DB instance and GORM not doing so either (see this issue) I couldn't figure out a way to test.

Is there a way or do I have to rethink my testing approach?

How to test INSERT statement?

In my code:

_, err = db.Exec("INSERT INTO mytable(a, b) VALUES(?,?)

and in my test:

sqlmock.ExpectExec("INSERT INTO mytable(a,b)")
             .WithArgs("A", "B")
            ....

But I get the message:

INSERT INTO mytable(a, b) VALUES(?,?) does not match regex INSERT INTO mytable(a,b)

I've tried adding the values to the expectation parameters and escaping the question marks but with no joy. Is this possible?

Expectations are not met

Hi,

i am mocking a insert statement but the expectation fails, event though i cannot see why.
The following error message reveal that the arguments are identical.

sql_writer_test.go:29: error was not expected while writing event: exec query 'INSERT INTO Event (SourceId, Created, EventType, Version, Payload) VALUES (?, ?, ?, ?, ?)', args [2eb5880e-4bfd-4450-92fc-df851bae5dbb 2016-02-03 22:05:00.712109 +0000 UTC TEST 1 {"version":1,"name":"Joe","balance":12.99,"birth_date":"2015-12-13T23:59:59+02:00"}] does not match expected [2eb5880e-4bfd-4450-92fc-df851bae5dbb 2016-02-03 22:05:00.712109 +0000 UTC TEST 1 {"version":1,"name":"Joe","balance":12.99,"birth_date":"2015-12-13T23:59:59+02:00"}]

Am i doing something wrong?

ExpectPrepare (and likely Expect*) fail when using `?` as parameter token not at end of query

When declaring ExpectPrepare for a query with a ? not at the end of the query, the test will fail, stating that the regex does not match.

Test code:

func TestPrepare() {
    sampleQuery := "SELECT * FROM accounts WHERE source = ? LIMIT 100"
    db, mock, err := sqlmock.New()
    mock.ExpectPrepare(sampleQuery)
    db.Prepare(sampleQuery)
}

Error:

query 'SELECT * FROM accounts WHERE source = ? LIMIT 100', does not match regex [SELECT * FROM accounts WHERE source = ? LIMIT 100]

This is, of course, due to the question mark in the sql, but that should be detected when creating the expectation.

By contrast, the following will succeed:

func TestPrepare() {
    sampleQuery := "SELECT * FROM accounts WHERE source = ?"
    db, mock, err := sqlmock.New()
    mock.ExpectPrepare(sampleQuery)
    db.Prepare(sampleQuery)
}

Force errors in other places

I would like to force errors in other places such as:

http://golang.org/pkg/database/sql/#Rows.Scan
http://golang.org/pkg/database/sql/#Rows.Columns

Are there any plans about this?

Testing lib/pq with RETURNING clause.

This is probably something silly, but I could't find a similar problem.

I have a query that must return the inserted id:

INSERT INTO AuctionUser (name, coins) VALUES ($1, $2) RETURNING id;

I'm using lib/pq, and it doesn't support the LastInsertId method.

Anyway, my test is failing at the point on which I decide the returning rows:

mock.ExpectQuery("INSERT INTO AuctionUser (.+) RETURNING id").
    WithArgs(mockUser.Name, mockUser.Coins).
    WillReturnRows(sqlmock.NewRows([]string{"id"}))

It says that it has no rows on result set.
Am I missing something?

duplicate driverConn close

Any function that calls rows.Close() causes a sql: duplicate driverConn close when calling db.Close() on the mock db object.

Sample code for illustration purposes

Sample function:

type MyDatabase struct {
    sql.DB
}

func (db *MyDatabase) GetThing(thing string) (data []byte, err error) {
    stmt := "SELECT ..."
    rows, err := db.Query(stmt, thing) 
    if err != nil {
        return nil, err 
    }   
    defer rows.Close()
    ... 
}

Sample test:

func TestMySQLGetTimestampKeyNoKey(t *testing.T) {
    db, _ := sqlmock.New()
    s := MyDatabase{DB: db}

    sqlmock.ExpectQuery(
        "SELECT `cipher`, `public` FROM `timestamp_keys` WHERE `gun`=\\?;",
    ).WithArgs("testGUN").WillReturnError(sql.ErrNoRows)

    foo, err = s.GetThing("testThing")
    ...

    err = db.Close()
    assert.Nil(t, err, "Expectation not met: %v", err)
}

Commenting out that defer rows.Close() stops the error being raised. I don't see anything in the go-sqlmock code that would make this happen and I don't have time to dig into it right now.

Disable/limit expectations?

Is it possible to disable or limit that expectations were met? I'm happy just to have a db mock which allows my transactional code to be unit tested. Is there another way I should be doing this?

QueryRow doesn't satisfy expectation

My function calls QueryRow, in the test I've set up an ExpectQuery as if it were a normal Query. The QueryRow does not appear to satisfy the expectation. Is that expected?

False positive with ExpectationsWereMet and wrong expectation execution order

I have functions that do multiple calls to ExpectedQuery in order to test the behavior of some of my functions that use those queries in that order. Something like this:

func mockFunc1(mock sqlmock.Sqlmock) {
	query1 := "..."
	query1Rows := sqlmock.NewRows([]string{"column1, column2"}).FromCSVString("value1, value2")
	mock.ExpectQuery(query1).WillReturnRows(query1Rows)
	
	query2 := "..."
	query2Rows := sqlmock.NewRows([]string{"column1"}).FromCSVString("value1")
	mock.ExpectQuery(query2).WillReturnRows(query2Rows)
}

func mockFunc2(mock sqlmock.Sqlmock) {
	query3 := "..."
	query3Rows := sqlmock.NewRows([]string{"column1"}).FromCSVString("value1")
	mock.ExpectQuery(query3).WillReturnRows(query3Rows)
}

Then I have the function that I want to test:

func myFunc(){
	callToQuery1()
	callToQuery2()
}

And finally, that is what I do on my test function:

func myFunctionTest(t *testing.T){
	Db_driver, mock, err := sqlmock.New()
	if err != nil {
		t.Log("Error on DB mock creation: ", err)
	}
	defer Db_driver.Close()
	mock.MatchExpectationsInOrder(true)
	mockFunc1(mock)
	mockFunc2(mock)

	myFunc()
	callToQuery3()

	err = mock.ExpectationsWereMet()
	if err != nil {
		t.Log(err, " expectations not met")
	}
}

What happens here is that if I call first mockFunc1(mock) and then mockFunc2(mock) the test will fail saying that "query3 does not match regex for query1", but if I call first mockFunc2(mock) and then mockFunc1(mock) all goes nicely and the test pass.

I inspected all the values that I retrieved from the mock when issuing the calls and the values are correct, but mock.ExpectationsWereMet() will say that "query1 expectation not met", which cannot be, since I got the values retrieved from that query.

So these are my two doubts. First, I don't know what I have to call the mockFuncs in reverse order (which I'm 100% sure that the queries in my code are executed in the order query1, query2, query3), and the second question is what ExpectationsWereMet states that query1 expectation was not met when it actually was (I also checked that every regex I stated is matching my queries). Thanks in advance.

Asserting on no queries

I'm trying to add a test that expects no queries to occur. I imagined this would be simply creating a mock db and it would raise an error if an unexpected query of some kind happened. It looks like it does, but it doesn't fail the test.

all expectations were already fulfilled, call to query 'SELECT store_id, address, topic FROM webhook WHERE id=0 AND active=1 LIMIT 1;' with args [] was not expected
PASS
ok      github.com/foo     0.011s

I'm assuming this would be something that needs to go in here to compare the count?

func (c *sqlmock) ExpectationsWereMet() error {

Create statements.

Create statement is attempting Regex for some reason, and is not matching on the same value I put in the Exec as I do in the ExpectedExec.

Error message:

Installation failed for MySQL adapater. exec query 'CREATE TABLE IF NOT EXISTS credential ( credential_id INT(10) NOT NULL AUTO_INCREMENT, username VARCHAR(255) NOT NULL, password TEXT NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY (credential_id), UNIQUE INDEX username_lookups (username ASC));', does not match regex 'CREATE TABLE IF NOT EXISTS credential ( credential_id INT(10) NOT NULL AUTO_INCREMENT, username VARCHAR(255) NOT NULL, password TEXT NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY (credential_id), UNIQUE INDEX username_lookups (username ASC));'

Example code:

const (
	installSQL = `CREATE TABLE IF NOT EXISTS credential (
    credential_id INT(10) NOT NULL AUTO_INCREMENT,
    username VARCHAR(255) NOT NULL,
    password TEXT NOT NULL,
    created_at DATETIME NOT NULL,
    PRIMARY KEY (credential_id),
    UNIQUE INDEX username_lookups (username ASC));`
)

func runInstallSQL(db *sql.DB) error {
	_, err := db.Exec(installSQL)
	return err
}

Example test:

func Test_runInstallSQL(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("cannot start mock sql for testing: %s", err)
	}
	defer db.Close()

	mock.ExpectExec(installSQL).WillReturnError(nil)
	assert.Nil(t, runInstallSQL(db))
	assert.Nil(t, mock.ExpectationsWereMet(), "Expectations of SQL queries was not met.")
}

How to expect a prepared insert ?


func TestLogHistory(t *testing.T) {
	var mock sqlmock.Sqlmock
	var err error
	db, mock, err = sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}

	mock.ExpectPrepare("INSERT INTO").ExpectExec().WithArgs("2")

	stmt, err := db.Prepare("INSERT INTO history VALUES(?)")
	if err != nil {
		panic(err)
	}

	if _, err := stmt.Exec("2"); err != nil {
		t.Fatal(err)
	}

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}
--- FAIL: TestLogHistory (0.00s)
	db_test.go:33: exec query 'INSERT INTO history VALUES(?)' with args [2], must return a database/sql/driver.result, but it was not set for expectation *sqlmock.ExpectedExec as ExpectedExec => expecting Exec which:
		  - matches sql: 'INSERT INTO'
		  - is with arguments:
		    0 - 2

I can't figure out how to setup the mock to expect an insert. Any tips ?

ExpectQuery in prepared statement fails if the argument is "2" ( or, putatively greater than 1)

Hi ,

The following test will fail:

package main

import (
	"database/sql"
	"fmt"
)

func testFunction(db *sql.DB) {
	stmt, err := db.Prepare("SELECT id FROM test WHERE id=$1")

	if err != nil {
		fmt.Println(err)
	}

	stmt.QueryRow("2")

	stmt.Close()

}

func main() {}

package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
	sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
)

func TestPrepare(t *testing.T) {
	db, mock, mErr := sqlmock.New()
	assert.NoError(t, mErr)
	defer db.Close()

	mock.ExpectPrepare("^SELECT id FROM test WHERE id=\\$1$")
	mock.ExpectQuery("2")

	testFunction(db)

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("there were unfulfilled expections: %s", err)
	}

}

If i replace the code with

mock.ExpectQuery("1")

and

stmt.QueryRow("1")

everything runs as expected.

I'm i doing something wrong?

Thanks

ExpectQuery does not appear to work with subqueries

My code defines the following query:

const sqlLatestFeed = `select feedid from feed where id = (select max(id) from feed)`

In my mock set up I expect to see this query - note that the same constant is used both in my code and in the mock setup in the unit test.

mock.ExpectQuery(sqlLatestFeed).WillReturnRows(rows)

When I run the test I get the following error.

Error:      Expected nil, but got: &errors.errorString{s:"query 'select feedid from feed where id = (select max(id) from feed)', does not match regex [select feedid from feed where id = (select max(id) from feed)]"}

If I substitute another query that does not include a subquery then I do not see the error.

Rows does not implement database/sql/driver.Rows

The new Rows struct is not backward compatible with the previous Rows interface; it's missing all of the driver.Rows methods. T

he changelog and commit message from #68 leads one to believe that was an inadvertent change.

escaping subqueries and other chars

Hi,

when I started adding tests for one of my db package I had the problem that my queries mostly all contained sub queries (and thus ( and )) were spread across multiple lines or contained other regex-relevant characters which then were treated by the regex engine which then resulted in lots of exec query 'rawQuery', does not match regex 'rawQuery'

After escaping the first bunch by hand I hacked together this small helper:

// small testing helper to escape `(`, `)` and `$` to not be used as regex modifiers
func escapeQueryToRegex(in string) (out string) {
    // replace multiple spaces, tabs and newlines with one space
    out = strings.Join(strings.Fields(in), " ")

    // slice of regex relevant characters which need to be escaled
    chars := []string{`(`, `)`, `$`, `+`}
    for _, r := range chars {
        out = strings.Replace(out, r, `\`+r, -1)
    }

    return out
}

I wonder if there is a smarter solution for this problem and if not would have no problem with you guys including this in go-sqlmock. It's an awesome package. Thanks!

How to test update & later select

I'm trying to test a method that is first updating a row using its ID and then reading the updated row using its ID:

_, err := DB.Exec("UPDATE baskets SET property = ? WHERE id = ?", prop, id)

err = DB.Get(basket, "SELECT id, property, created_at, updated_at FROM baskets WHERE id = ?", id)

For mocking in the unit tests, I have:

rows = sqlmock.NewRows([]string{"id", "property", "created_at", "updated_at"}).AddRow(id, oldProp, timeNow, timeNow)
mock.ExpectExec("UPDATE baskets").WithArgs(newProp, id).WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectQuery("^SELECT (.+) FROM baskets WHERE").WithArgs(id).WillReturnRows(rows)

The second ExpectQuery obviously fails because the oldProp is updated to newProp. So how can I test my function by mocking row update and then running a select query?

N.B. I'm using sqlx methods above, which seem to work fine with go-sqlmock lib.

Expect Close to be called

It'd be nice if the mock rows struct would expect Close() to be called, either by default or with an ExpectClose() enabler. Most of the time the closing is deferred, but we want to test that it happens.

Expectations are not checked if database is not used at all

The following test will pass even if it shouldn't:

func TestSomething(t *testing.T) {
    db, err := sql.Open("mock", "")
    if err != nil {
        t.Fatalf("Could not open mock database: %v", err)
    }
    sqlmock.ExpectExec("DELETE foo").WillReturnResult(sqlmock.NewResult(1, 1))
    if err = db.Close(); err != nil {
        t.Fatalf("Could not close database: %v", err)
    }
}

If I do something with the database, this will fail as expected:

func TestSomething(t *testing.T) {
    db, err := sql.Open("mock", "")
    if err != nil {
        t.Fatalf("Could not open mock database: %v", err)
    }
    sqlmock.ExpectExec("DELETE foo").WillReturnResult(sqlmock.NewResult(1, 1))
    db.Begin() //db.Exec works as well
    if err = db.Close(); err != nil {
        t.Fatalf("Could not close database: %v", err)
    }
}

Allow generous matching when SELECTing nondeterministic data

Thanks for an awesome library. I couldn't find a solution to this issue, and hope there's a trivial answer I couldn't reach.

I'm trying to test a function that executes a SELECT query which will return a row that contains a nondeterministic datum. The nondeterministic datum could be as simple as a timestamp, or as complex as a system process information that was inspected at the moment of INSERT.

Executing a query without Expect... is forbidden:

all expectations were already fulfilled, call to query '...'  was not expected

The Expect... series, such as ExpectQuery, requires a call to WillReturnRows or WillReturnError. Otherwise, it seems, it's an error:

query '...' must return a database/sql/driver.rows, but it was not set for expectation
*sqlmock.ExpectedQuery as ExpectedQuery => expecting Query or QueryRow
...

But WillReturnRows requires a precise row that will be used for an exact matching. The trouble is, I don't know what the precise row would be, because it contains a nondeterministic field.

There is a guide for matching arguments in README.md, but they are used for matching the query - what is to be sent to the database. I need a generous way to match the query result - what is to be received from the database.

Naturally, the solution for arguments doesn't work for matching the query result. The following:

rows := sqlmock.NewRows([]string{
    "id", "some_nondeterministic_field"}).
    AddRow(1, sqlmock.AnyArg())
mock.ExpectQuery(readQuery).WillReturnRows(rows)

is an error:

sql: Scan error on column index 1: converting driver.Value type sqlmock.anyArgument (\"{}\") to a uint64: invalid syntax

In fact I'd be happy to let sqlmock not do any kind of matching at all. I'd be grateful if I could just execute queries to the db object, get the result from my function, and do the matching myself.

Any idea?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.