mirror of
https://github.com/DATA-DOG/go-sqlmock.git
synced 2025-05-15 22:06:43 +02:00
allow to set errors for rows scan
* 9a36c2e more examples and tests for rows * 908877e cannot mock rows.Columns error so far
This commit is contained in:
parent
a071483cba
commit
dc0efdab8f
@ -2,6 +2,7 @@ package sqlmock
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -71,3 +72,13 @@ func TestQueryExpectationSqlMatch(t *testing.T) {
|
|||||||
t.Errorf("Sql must have matched the query")
|
t.Errorf("Sql must have matched the query")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleExpectExec() {
|
||||||
|
db, mock, _ := New()
|
||||||
|
result := NewErrorResult(fmt.Errorf("some error"))
|
||||||
|
mock.ExpectExec("^INSERT (.+)").WillReturnResult(result)
|
||||||
|
res, _ := db.Exec("INSERT something")
|
||||||
|
_, err := res.LastInsertId()
|
||||||
|
fmt.Println(err)
|
||||||
|
// Output: some error
|
||||||
|
}
|
||||||
|
@ -9,14 +9,21 @@ import (
|
|||||||
var mock = &Sqlmock{}
|
var mock = &Sqlmock{}
|
||||||
|
|
||||||
func ExampleNewErrorResult() {
|
func ExampleNewErrorResult() {
|
||||||
|
db, mock, _ := New()
|
||||||
result := NewErrorResult(fmt.Errorf("some error"))
|
result := NewErrorResult(fmt.Errorf("some error"))
|
||||||
mock.ExpectExec("^INSERT (.+)").WillReturnResult(result)
|
mock.ExpectExec("^INSERT (.+)").WillReturnResult(result)
|
||||||
|
res, _ := db.Exec("INSERT something")
|
||||||
|
_, err := res.LastInsertId()
|
||||||
|
fmt.Println(err)
|
||||||
|
// Output: some error
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleNewResult() {
|
func ExampleNewResult() {
|
||||||
var lastInsertID, affected int64
|
var lastInsertID, affected int64
|
||||||
result := NewResult(lastInsertID, affected)
|
result := NewResult(lastInsertID, affected)
|
||||||
mock.ExpectExec("^INSERT (.+)").WillReturnResult(result)
|
mock.ExpectExec("^INSERT (.+)").WillReturnResult(result)
|
||||||
|
fmt.Println(mock.ExpectationsWereMet())
|
||||||
|
// Output: there is a remaining expectation *sqlmock.ExpectedExec which was not matched yet
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldReturnValidSqlDriverResult(t *testing.T) {
|
func TestShouldReturnValidSqlDriverResult(t *testing.T) {
|
||||||
|
44
rows.go
44
rows.go
@ -17,19 +17,35 @@ type Rows interface {
|
|||||||
// return the same instance to perform subsequent actions.
|
// return the same instance to perform subsequent actions.
|
||||||
// Note that the number of values must match the number
|
// Note that the number of values must match the number
|
||||||
// of columns
|
// of columns
|
||||||
AddRow(...driver.Value) Rows
|
AddRow(columns ...driver.Value) Rows
|
||||||
|
|
||||||
// FromCSVString build rows from csv string.
|
// FromCSVString build rows from csv string.
|
||||||
// return the same instance to perform subsequent actions.
|
// return the same instance to perform subsequent actions.
|
||||||
// Note that the number of values must match the number
|
// Note that the number of values must match the number
|
||||||
// of columns
|
// of columns
|
||||||
FromCSVString(s string) Rows
|
FromCSVString(s string) Rows
|
||||||
|
|
||||||
|
// RowError allows to set an error
|
||||||
|
// which will be returned when a given
|
||||||
|
// row number is read
|
||||||
|
RowError(row int, err error) Rows
|
||||||
|
|
||||||
|
// CloseError allows to set an error
|
||||||
|
// which will be returned by rows.Close
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// The close error will be triggered only in cases
|
||||||
|
// when rows.Next() EOF was not yet reached, that is
|
||||||
|
// a default sql library behavior
|
||||||
|
CloseError(err error) Rows
|
||||||
}
|
}
|
||||||
|
|
||||||
type rows struct {
|
type rows struct {
|
||||||
cols []string
|
cols []string
|
||||||
rows [][]driver.Value
|
rows [][]driver.Value
|
||||||
pos int
|
pos int
|
||||||
|
scanErr map[int]error
|
||||||
|
closeErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rows) Columns() []string {
|
func (r *rows) Columns() []string {
|
||||||
@ -37,11 +53,7 @@ func (r *rows) Columns() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *rows) Close() error {
|
func (r *rows) Close() error {
|
||||||
return nil
|
return r.closeErr
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rows) Err() error {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// advances to next row
|
// advances to next row
|
||||||
@ -55,14 +67,24 @@ func (r *rows) Next(dest []driver.Value) error {
|
|||||||
dest[i] = col
|
dest[i] = col
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return r.scanErr[r.pos-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRows allows Rows to be created from a
|
// NewRows allows Rows to be created from a
|
||||||
// sql driver.Value slice or from the CSV string and
|
// sql driver.Value slice or from the CSV string and
|
||||||
// to be used as sql driver.Rows
|
// to be used as sql driver.Rows
|
||||||
func NewRows(columns []string) Rows {
|
func NewRows(columns []string) Rows {
|
||||||
return &rows{cols: columns}
|
return &rows{cols: columns, scanErr: make(map[int]error)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rows) CloseError(err error) Rows {
|
||||||
|
r.closeErr = err
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rows) RowError(row int, err error) Rows {
|
||||||
|
r.scanErr[row] = err
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rows) AddRow(values ...driver.Value) Rows {
|
func (r *rows) AddRow(values ...driver.Value) Rows {
|
||||||
|
209
rows_test.go
Normal file
209
rows_test.go
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
package sqlmock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleRows() {
|
||||||
|
db, mock, err := New()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to open sqlmock database:", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
rows := NewRows([]string{"id", "title"}).
|
||||||
|
AddRow(1, "one").
|
||||||
|
AddRow(2, "two")
|
||||||
|
|
||||||
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
||||||
|
|
||||||
|
rs, _ := db.Query("SELECT")
|
||||||
|
defer rs.Close()
|
||||||
|
|
||||||
|
for rs.Next() {
|
||||||
|
var id int
|
||||||
|
var title string
|
||||||
|
rs.Scan(&id, &title)
|
||||||
|
fmt.Println("scanned id:", id, "and title:", title)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Err() != nil {
|
||||||
|
fmt.Println("got rows error:", rs.Err())
|
||||||
|
}
|
||||||
|
// Output: scanned id: 1 and title: one
|
||||||
|
// scanned id: 2 and title: two
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRows_rowError() {
|
||||||
|
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
|
||||||
|
rs.Scan(&id, &title)
|
||||||
|
fmt.Println("scanned id:", id, "and title:", title)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Err() != nil {
|
||||||
|
fmt.Println("got rows error:", rs.Err())
|
||||||
|
}
|
||||||
|
// Output: scanned id: 0 and title: one
|
||||||
|
// got rows error: row error
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRows_closeError() {
|
||||||
|
db, mock, err := New()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to open sqlmock database:", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
rows := NewRows([]string{"id", "title"}).CloseError(fmt.Errorf("close error"))
|
||||||
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
||||||
|
|
||||||
|
rs, _ := db.Query("SELECT")
|
||||||
|
|
||||||
|
// Note: that close will return error only before rows EOF
|
||||||
|
// that is a default sql package behavior. If you run rs.Next()
|
||||||
|
// it will handle the error internally and return nil bellow
|
||||||
|
if err := rs.Close(); err != nil {
|
||||||
|
fmt.Println("got error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output: got error: close error
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllowsToSetRowsErrors(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
db, mock, err := New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
rows := NewRows([]string{"id", "title"}).
|
||||||
|
AddRow(0, "one").
|
||||||
|
AddRow(1, "two").
|
||||||
|
RowError(1, fmt.Errorf("error"))
|
||||||
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
||||||
|
|
||||||
|
rs, err := db.Query("SELECT")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
defer rs.Close()
|
||||||
|
|
||||||
|
if !rs.Next() {
|
||||||
|
t.Fatal("expected the first row to be available")
|
||||||
|
}
|
||||||
|
if rs.Err() != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", rs.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Next() {
|
||||||
|
t.Fatal("was not expecting the second row, since there should be an error")
|
||||||
|
}
|
||||||
|
if rs.Err() == nil {
|
||||||
|
t.Fatal("expected an error, but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRowsCloseError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
db, mock, err := New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
rows := NewRows([]string{"id"}).CloseError(fmt.Errorf("close error"))
|
||||||
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
||||||
|
|
||||||
|
rs, err := db.Query("SELECT")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rs.Close(); err == nil {
|
||||||
|
t.Fatal("expected a close error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuerySingleRow(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
db, mock, err := New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
rows := NewRows([]string{"id"}).
|
||||||
|
AddRow(1).
|
||||||
|
AddRow(2)
|
||||||
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
||||||
|
|
||||||
|
var id int
|
||||||
|
if err := db.QueryRow("SELECT").Scan(&id); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.ExpectQuery("SELECT").WillReturnRows(NewRows([]string{"id"}))
|
||||||
|
if err := db.QueryRow("SELECT").Scan(&id); err != sql.ErrNoRows {
|
||||||
|
t.Fatal("expected sql no rows error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO: the only way to mock columns error would be
|
||||||
|
// to return nil instead of rows, but rows.Close will
|
||||||
|
// panic due to a bug in go sql package
|
||||||
|
func TODO_TestRowsColumnsError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
db, mock, err := New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
mock.ExpectQuery("SELECT").WillReturnRows(nil)
|
||||||
|
|
||||||
|
rs, err := db.Query("SELECT")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = rs.Columns()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error for columns")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
@ -201,6 +201,7 @@ func (c *Sqlmock) Query(query string, args []driver.Value) (rw driver.Rows, err
|
|||||||
return nil, t.err // mocked to return error
|
return nil, t.err // mocked to return error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove when rows_test.go:186 is available, won't cause a BC
|
||||||
if t.rows == nil {
|
if t.rows == nil {
|
||||||
return nil, fmt.Errorf("query '%s' with args %+v, must return a database/sql/driver.rows, but it was not set for expectation %T as %+v", query, args, t, t)
|
return nil, fmt.Errorf("query '%s' with args %+v, must return a database/sql/driver.rows, but it was not set for expectation %T as %+v", query, args, t, t)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user