Skip to content

Commit f7b0b93

Browse files
committed
closes #114 allow expecting rows to be closed
1 parent a6e6646 commit f7b0b93

File tree

7 files changed

+70
-4
lines changed

7 files changed

+70
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ It only asserts that argument is of `time.Time` type.
213213

214214
## Change Log
215215

216+
- **2018-12-11** - added expectation of Rows to be closed, while mocking expected query.
216217
- **2018-12-11** - introduced an option to provide **QueryMatcher** in order to customize SQL query matching.
217218
- **2017-09-01** - it is now possible to expect that prepared statement will be closed,
218219
using **ExpectedPrepare.WillBeClosed**.

expectations.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,10 @@ func (e *ExpectedRollback) String() string {
125125
// Returned by *Sqlmock.ExpectQuery.
126126
type ExpectedQuery struct {
127127
queryBasedExpectation
128-
rows driver.Rows
129-
delay time.Duration
128+
rows driver.Rows
129+
delay time.Duration
130+
rowsMustBeClosed bool
131+
rowsWereClosed bool
130132
}
131133

132134
// WithArgs will match given expected args to actual database query arguments.
@@ -137,6 +139,12 @@ func (e *ExpectedQuery) WithArgs(args ...driver.Value) *ExpectedQuery {
137139
return e
138140
}
139141

142+
// RowsWillBeClosed expects this query rows to be closed.
143+
func (e *ExpectedQuery) RowsWillBeClosed() *ExpectedQuery {
144+
e.rowsMustBeClosed = true
145+
return e
146+
}
147+
140148
// WillReturnError allows to set an error for expected database query
141149
func (e *ExpectedQuery) WillReturnError(err error) *ExpectedQuery {
142150
e.err = err

expectations_before_go18.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
// WillReturnRows specifies the set of resulting rows that will be returned
1212
// by the triggered query
1313
func (e *ExpectedQuery) WillReturnRows(rows *Rows) *ExpectedQuery {
14-
e.rows = &rowSets{sets: []*Rows{rows}}
14+
e.rows = &rowSets{sets: []*Rows{rows}, ex: e}
1515
return e
1616
}
1717

expectations_go18.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func (e *ExpectedQuery) WillReturnRows(rows ...*Rows) *ExpectedQuery {
1616
for i, r := range rows {
1717
sets[i] = r
1818
}
19-
e.rows = &rowSets{sets: sets}
19+
e.rows = &rowSets{sets: sets, ex: e}
2020
return e
2121
}
2222

rows.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ var CSVColumnParser = func(s string) []byte {
2222
type rowSets struct {
2323
sets []*Rows
2424
pos int
25+
ex *ExpectedQuery
2526
}
2627

2728
func (rs *rowSets) Columns() []string {
2829
return rs.sets[rs.pos].cols
2930
}
3031

3132
func (rs *rowSets) Close() error {
33+
rs.ex.rowsWereClosed = true
3234
return rs.sets[rs.pos].closeErr
3335
}
3436

rows_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,29 @@ func ExampleRows_closeError() {
8888
// Output: got error: close error
8989
}
9090

91+
func ExampleRows_expectToBeClosed() {
92+
db, mock, err := New()
93+
if err != nil {
94+
fmt.Println("failed to open sqlmock database:", err)
95+
}
96+
defer db.Close()
97+
98+
rows := NewRows([]string{"id", "title"}).AddRow(1, "john")
99+
mock.ExpectQuery("SELECT").WillReturnRows(rows).RowsWillBeClosed()
100+
101+
db.Query("SELECT")
102+
103+
if err := mock.ExpectationsWereMet(); err != nil {
104+
fmt.Println("got error:", err)
105+
}
106+
107+
// Output: got error: expected query rows to be closed, but it was not: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
108+
// - matches sql: 'SELECT'
109+
// - is without arguments
110+
// - should return rows:
111+
// row 0 - [1 john]
112+
}
113+
91114
func ExampleRows_customDriverValue() {
92115
db, mock, err := New()
93116
if err != nil {
@@ -184,6 +207,31 @@ func TestRowsCloseError(t *testing.T) {
184207
}
185208
}
186209

210+
func TestRowsClosed(t *testing.T) {
211+
t.Parallel()
212+
db, mock, err := New()
213+
if err != nil {
214+
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
215+
}
216+
defer db.Close()
217+
218+
rows := NewRows([]string{"id"}).AddRow(1)
219+
mock.ExpectQuery("SELECT").WillReturnRows(rows).RowsWillBeClosed()
220+
221+
rs, err := db.Query("SELECT")
222+
if err != nil {
223+
t.Fatalf("unexpected error: %s", err)
224+
}
225+
226+
if err := rs.Close(); err != nil {
227+
t.Fatalf("unexpected error: %v", err)
228+
}
229+
230+
if err := mock.ExpectationsWereMet(); err != nil {
231+
t.Fatal(err)
232+
}
233+
}
234+
187235
func TestQuerySingleRow(t *testing.T) {
188236
t.Parallel()
189237
db, mock, err := New()

sqlmock.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ func (c *sqlmock) ExpectationsWereMet() error {
176176
return fmt.Errorf("expected prepared statement to be closed, but it was not: %s", prep)
177177
}
178178
}
179+
180+
// must check whether all expected queried rows are closed
181+
if query, ok := e.(*ExpectedQuery); ok {
182+
if query.rowsMustBeClosed && !query.rowsWereClosed {
183+
return fmt.Errorf("expected query rows to be closed, but it was not: %s", query)
184+
}
185+
}
179186
}
180187
return nil
181188
}

0 commit comments

Comments
 (0)