Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions sqlmock_before_go18.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func (c *sqlmock) query(query string, args []namedValue) (*ExpectedQuery, error)
var expected *ExpectedQuery
var fulfilled int
var ok bool
var argMismatch error
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
Expand All @@ -74,6 +75,10 @@ func (c *sqlmock) query(query string, args []namedValue) (*ExpectedQuery, error)
if err := qr.attemptArgMatch(args); err == nil {
expected = qr
break
} else {
// remember the reason the sql matched but the args did not,
// so it can be reported if no other expectation matches
argMismatch = err
}
}
next.Unlock()
Expand All @@ -84,7 +89,11 @@ func (c *sqlmock) query(query string, args []namedValue) (*ExpectedQuery, error)
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg, query, args)
err := fmt.Errorf(msg, query, args)
if argMismatch != nil {
err = fmt.Errorf("%s, arguments do not match: %s", err, argMismatch)
}
return nil, err
}

defer expected.Unlock()
Expand Down Expand Up @@ -133,6 +142,7 @@ func (c *sqlmock) exec(query string, args []namedValue) (*ExpectedExec, error) {
var expected *ExpectedExec
var fulfilled int
var ok bool
var argMismatch error
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
Expand All @@ -157,6 +167,10 @@ func (c *sqlmock) exec(query string, args []namedValue) (*ExpectedExec, error) {
if err := exec.attemptArgMatch(args); err == nil {
expected = exec
break
} else {
// remember the reason the sql matched but the args did not,
// so it can be reported if no other expectation matches
argMismatch = err
}
}
next.Unlock()
Expand All @@ -166,7 +180,11 @@ func (c *sqlmock) exec(query string, args []namedValue) (*ExpectedExec, error) {
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg, query, args)
err := fmt.Errorf(msg, query, args)
if argMismatch != nil {
err = fmt.Errorf("%s, arguments do not match: %s", err, argMismatch)
}
return nil, err
}
defer expected.Unlock()

Expand Down
22 changes: 20 additions & 2 deletions sqlmock_go18.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ func (c *sqlmock) query(query string, args []driver.NamedValue) (*ExpectedQuery,
var expected *ExpectedQuery
var fulfilled int
var ok bool
var argMismatch error
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
Expand All @@ -223,6 +224,10 @@ func (c *sqlmock) query(query string, args []driver.NamedValue) (*ExpectedQuery,
if err := qr.attemptArgMatch(args); err == nil {
expected = qr
break
} else {
// remember the reason the sql matched but the args did not,
// so it can be reported if no other expectation matches
argMismatch = err
}
}
next.Unlock()
Expand All @@ -233,7 +238,11 @@ func (c *sqlmock) query(query string, args []driver.NamedValue) (*ExpectedQuery,
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg, query, args)
err := fmt.Errorf(msg, query, args)
if argMismatch != nil {
err = fmt.Errorf("%s, arguments do not match: %s", err, argMismatch)
}
return nil, err
}

defer expected.Unlock()
Expand Down Expand Up @@ -283,6 +292,7 @@ func (c *sqlmock) exec(query string, args []driver.NamedValue) (*ExpectedExec, e
var expected *ExpectedExec
var fulfilled int
var ok bool
var argMismatch error
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
Expand All @@ -307,6 +317,10 @@ func (c *sqlmock) exec(query string, args []driver.NamedValue) (*ExpectedExec, e
if err := exec.attemptArgMatch(args); err == nil {
expected = exec
break
} else {
// remember the reason the sql matched but the args did not,
// so it can be reported if no other expectation matches
argMismatch = err
}
}
next.Unlock()
Expand All @@ -316,7 +330,11 @@ func (c *sqlmock) exec(query string, args []driver.NamedValue) (*ExpectedExec, e
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg, query, args)
err := fmt.Errorf(msg, query, args)
if argMismatch != nil {
err = fmt.Errorf("%s, arguments do not match: %s", err, argMismatch)
}
return nil, err
}
defer expected.Unlock()

Expand Down
39 changes: 39 additions & 0 deletions sqlmock_go18_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,49 @@ import (
"context"
"database/sql"
"errors"
"strings"
"testing"
"time"
)

// When expectations are matched out of order and a query matches by sql but
// not by arguments, the failure should explain the argument mismatch and
// include the actual received values, not just report "was not expected".
func TestUnorderedArgumentMismatchReportsReceivedValues(t *testing.T) {
t.Parallel()
db, mock, err := New()
if err != nil {
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()

mock.MatchExpectationsInOrder(false)

mock.ExpectExec("UPDATE users").WithArgs("alice").WillReturnResult(NewResult(1, 1))
if _, err := db.Exec("UPDATE users SET name = ?", "bob"); err == nil {
t.Error("expected an error for the mismatched exec argument, but got none")
} else {
if !strings.Contains(err.Error(), "arguments do not match") {
t.Errorf("expected the error to explain the argument mismatch, but got: %s", err)
}
if !strings.Contains(err.Error(), "bob") {
t.Errorf("expected the error to include the received argument value 'bob', but got: %s", err)
}
}

mock.ExpectQuery("SELECT name FROM users").WithArgs(1).WillReturnRows(NewRows([]string{"name"}).AddRow("alice"))
if _, err := db.Query("SELECT name FROM users WHERE id = ?", 2); err == nil {
t.Error("expected an error for the mismatched query argument, but got none")
} else {
if !strings.Contains(err.Error(), "arguments do not match") {
t.Errorf("expected the error to explain the argument mismatch, but got: %s", err)
}
if !strings.Contains(err.Error(), "actual [int64 - 2]") {
t.Errorf("expected the error to include the received argument value 2, but got: %s", err)
}
}
}

func TestContextExecCancel(t *testing.T) {
t.Parallel()
db, mock, err := New()
Expand Down