Skip to content

Commit e179163

Browse files
author
Nikita Koryabkin
committed
Merge branch 'master' of github.com:DATA-DOG/go-sqlmock
2 parents f3f5a5d + b91d98d commit e179163

File tree

7 files changed

+340
-14
lines changed

7 files changed

+340
-14
lines changed

expectations.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,3 +338,47 @@ type queryBasedExpectation struct {
338338
converter driver.ValueConverter
339339
args []driver.Value
340340
}
341+
342+
func (e *queryBasedExpectation) attemptArgMatch(args []namedValue) (err error) {
343+
// catch panic
344+
defer func() {
345+
if e := recover(); e != nil {
346+
_, ok := e.(error)
347+
if !ok {
348+
err = fmt.Errorf(e.(string))
349+
}
350+
}
351+
}()
352+
353+
err = e.argsMatches(args)
354+
return
355+
}
356+
357+
// ExpectedPing is used to manage *sql.DB.Ping expectations.
358+
// Returned by *Sqlmock.ExpectPing.
359+
type ExpectedPing struct {
360+
commonExpectation
361+
delay time.Duration
362+
}
363+
364+
// WillDelayFor allows to specify duration for which it will delay result. May
365+
// be used together with Context.
366+
func (e *ExpectedPing) WillDelayFor(duration time.Duration) *ExpectedPing {
367+
e.delay = duration
368+
return e
369+
}
370+
371+
// WillReturnError allows to set an error for expected database ping
372+
func (e *ExpectedPing) WillReturnError(err error) *ExpectedPing {
373+
e.err = err
374+
return e
375+
}
376+
377+
// String returns string representation
378+
func (e *ExpectedPing) String() string {
379+
msg := "ExpectedPing => expecting database Ping"
380+
if e.err != nil {
381+
msg += fmt.Sprintf(", which should return error: %s", e.err)
382+
}
383+
return msg
384+
}

options.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,19 @@ func QueryMatcherOption(queryMatcher QueryMatcher) func(*sqlmock) error {
2020
return nil
2121
}
2222
}
23+
24+
// MonitorPingsOption determines whether calls to Ping on the driver should be
25+
// observed and mocked.
26+
//
27+
// If true is passed, we will check these calls were expected. Expectations can
28+
// be registered using the ExpectPing() method on the mock.
29+
//
30+
// If false is passed or this option is omitted, calls to Ping will not be
31+
// considered when determining expectations and calls to ExpectPing will have
32+
// no effect.
33+
func MonitorPingsOption(monitorPings bool) func(*sqlmock) error {
34+
return func(s *sqlmock) error {
35+
s.monitorPings = monitorPings
36+
return nil
37+
}
38+
}

sqlmock.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
// for any kind of database action in order to mock
2222
// and test real database behavior.
2323
type Sqlmock interface {
24-
2524
// ExpectClose queues an expectation for this database
2625
// action to be triggered. the *ExpectedClose allows
2726
// to mock database response
@@ -57,6 +56,17 @@ type Sqlmock interface {
5756
// the *ExpectedRollback allows to mock database response
5857
ExpectRollback() *ExpectedRollback
5958

59+
// ExpectPing expected *sql.DB.Ping to be called.
60+
// the *ExpectedPing allows to mock database response
61+
//
62+
// Ping support only exists in the SQL library in Go 1.8 and above.
63+
// ExpectPing in Go <=1.7 will return an ExpectedPing but not register
64+
// any expectations.
65+
//
66+
// You must enable pings using MonitorPingsOption for this to register
67+
// any expectations.
68+
ExpectPing() *ExpectedPing
69+
6070
// MatchExpectationsInOrder gives an option whether to match all
6171
// expectations in the order they were set or not.
6272
//
@@ -83,6 +93,7 @@ type sqlmock struct {
8393
drv *mockDriver
8494
converter driver.ValueConverter
8595
queryMatcher QueryMatcher
96+
monitorPings bool
8697

8798
expected []expectation
8899
}
@@ -104,6 +115,15 @@ func (c *sqlmock) open(options []func(*sqlmock) error) (*sql.DB, Sqlmock, error)
104115
if c.queryMatcher == nil {
105116
c.queryMatcher = QueryMatcherRegexp
106117
}
118+
119+
if c.monitorPings {
120+
// We call Ping on the driver shortly to verify startup assertions by
121+
// driving internal behaviour of the sql standard library. We don't
122+
// want this call to ping to be monitored for expectation purposes so
123+
// temporarily disable.
124+
c.monitorPings = false
125+
defer func() { c.monitorPings = true }()
126+
}
107127
return db, c, db.Ping()
108128
}
109129

sqlmock_before_go18.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
// +build !go1.8
22

3-
/*
4-
Package sqlmock is a mock library implementing sql driver. Which has one and only
5-
purpose - to simulate any sql driver behavior in tests, without needing a real
6-
database connection. It helps to maintain correct **TDD** workflow.
3+
package sqlmock
74

8-
It does not require any modifications to your source code in order to test
9-
and mock database operations. Supports concurrency and multiple database mocking.
5+
import "log"
106

11-
The driver allows to mock any sql driver method behavior.
12-
*/
13-
package sqlmock
7+
func (c *sqlmock) ExpectPing() *ExpectedPing {
8+
log.Println("ExpectPing has no effect on Go 1.7 or below")
9+
return &ExpectedPing{}
10+
}
1411

1512
func (c *sqlmock) exec(query string, args []namedValue) (*ExpectedExec, error) {
1613
var expected *ExpectedExec

sqlmock_before_go18_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// +build !go1.8
2+
3+
package sqlmock
4+
5+
import (
6+
"fmt"
7+
"testing"
8+
"time"
9+
)
10+
11+
func TestSqlmockExpectPingHasNoEffect(t *testing.T) {
12+
db, mock, err := New()
13+
if err != nil {
14+
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
15+
}
16+
defer db.Close()
17+
18+
e := mock.ExpectPing()
19+
20+
// Methods on the expectation can be called
21+
e.WillDelayFor(time.Hour).WillReturnError(fmt.Errorf("an error"))
22+
23+
if err = mock.ExpectationsWereMet(); err != nil {
24+
t.Errorf("expected no error to be returned, but got '%s'", err)
25+
}
26+
}

sqlmock_go18.go

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"context"
77
"database/sql/driver"
88
"errors"
9+
"fmt"
10+
"log"
911
"time"
1012
)
1113

@@ -85,11 +87,57 @@ func (c *sqlmock) PrepareContext(ctx context.Context, query string) (driver.Stmt
8587
return nil, err
8688
}
8789

88-
// Implement the "Pinger" interface
89-
// for now we do not have a Ping expectation
90-
// may be something for the future
90+
// Implement the "Pinger" interface - the explicit DB driver ping was only added to database/sql in Go 1.8
9191
func (c *sqlmock) Ping(ctx context.Context) error {
92-
return nil
92+
if !c.monitorPings {
93+
return nil
94+
}
95+
96+
ex, err := c.ping()
97+
if ex != nil {
98+
select {
99+
case <-ctx.Done():
100+
return ErrCancelled
101+
case <-time.After(ex.delay):
102+
}
103+
}
104+
105+
return err
106+
}
107+
108+
func (c *sqlmock) ping() (*ExpectedPing, error) {
109+
var expected *ExpectedPing
110+
var fulfilled int
111+
var ok bool
112+
for _, next := range c.expected {
113+
next.Lock()
114+
if next.fulfilled() {
115+
next.Unlock()
116+
fulfilled++
117+
continue
118+
}
119+
120+
if expected, ok = next.(*ExpectedPing); ok {
121+
break
122+
}
123+
124+
next.Unlock()
125+
if c.ordered {
126+
return nil, fmt.Errorf("call to database Ping, was not expected, next expectation is: %s", next)
127+
}
128+
}
129+
130+
if expected == nil {
131+
msg := "call to database Ping was not expected"
132+
if fulfilled == len(c.expected) {
133+
msg = "all expectations were already fulfilled, " + msg
134+
}
135+
return nil, fmt.Errorf(msg)
136+
}
137+
138+
expected.triggered = true
139+
expected.Unlock()
140+
return expected, expected.err
93141
}
94142

95143
// Implement the "StmtExecContext" interface
@@ -102,4 +150,14 @@ func (stmt *statement) QueryContext(ctx context.Context, args []driver.NamedValu
102150
return stmt.conn.QueryContext(ctx, stmt.query, args)
103151
}
104152

153+
func (c *sqlmock) ExpectPing() *ExpectedPing {
154+
if !c.monitorPings {
155+
log.Println("ExpectPing will have no effect as monitoring pings is disabled. Use MonitorPingsOption to enable.")
156+
return nil
157+
}
158+
e := &ExpectedPing{}
159+
c.expected = append(c.expected, e)
160+
return e
161+
}
162+
105163
// @TODO maybe add ExpectedBegin.WithOptions(driver.TxOptions)

0 commit comments

Comments
 (0)