package sqlmock import ( "bytes" "database/sql" "database/sql/driver" "fmt" "testing" ) const invalid = `☠☠☠ MEMORY OVERWRITTEN ☠☠☠ ` 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 ExampleRows_rawBytes() { db, mock, err := New() if err != nil { fmt.Println("failed to open sqlmock database:", err) } defer db.Close() rows := NewRows([]string{"id", "binary"}). AddRow(1, []byte(`one binary value with some text!`)). AddRow(2, []byte(`two binary value with even more text than the first one`)) mock.ExpectQuery("SELECT").WillReturnRows(rows) rs, _ := db.Query("SELECT") defer rs.Close() type scanned struct { id int raw sql.RawBytes } fmt.Println("initial read...") var ss []scanned for rs.Next() { var s scanned rs.Scan(&s.id, &s.raw) ss = append(ss, s) fmt.Println("scanned id:", s.id, "and raw:", string(s.raw)) } if rs.Err() != nil { fmt.Println("got rows error:", rs.Err()) } fmt.Println("after reading all...") for _, s := range ss { fmt.Println("scanned id:", s.id, "and raw:", string(s.raw)) } // Output: // initial read... // scanned id: 1 and raw: one binary value with some text! // scanned id: 2 and raw: two binary value with even more text than the first one // after reading all... // scanned id: 1 and raw: ☠☠☠ MEMORY OVERWRITTEN ☠ // scanned id: 2 and raw: ☠☠☠ MEMORY OVERWRITTEN ☠☠☠ ☠☠☠ MEMORY } func ExampleRows_expectToBeClosed() { 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, "john") mock.ExpectQuery("SELECT").WillReturnRows(rows).RowsWillBeClosed() db.Query("SELECT") if err := mock.ExpectationsWereMet(); err != nil { fmt.Println("got error:", err) } // Output: got error: expected query rows to be closed, but it was not: ExpectedQuery => expecting Query, QueryContext or QueryRow which: // - matches sql: 'SELECT' // - is without arguments // - should return rows: // row 0 - [1 john] } func ExampleRows_customDriverValue() { db, mock, err := New() if err != nil { fmt.Println("failed to open sqlmock database:", err) } defer db.Close() rows := NewRows([]string{"id", "null_int"}). AddRow(1, 7). AddRow(5, sql.NullInt64{Int64: 5, Valid: true}). AddRow(2, sql.NullInt64{}) mock.ExpectQuery("SELECT").WillReturnRows(rows) rs, _ := db.Query("SELECT") defer rs.Close() for rs.Next() { var id int var num sql.NullInt64 rs.Scan(&id, &num) fmt.Println("scanned id:", id, "and null int64:", num) } if rs.Err() != nil { fmt.Println("got rows error:", rs.Err()) } // Output: scanned id: 1 and null int64: {7 true} // scanned id: 5 and null int64: {5 true} // scanned id: 2 and null int64: {0 false} } 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 TestRowsClosed(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) mock.ExpectQuery("SELECT").WillReturnRows(rows).RowsWillBeClosed() rs, err := db.Query("SELECT") if err != nil { t.Fatalf("unexpected error: %s", err) } if err := rs.Close(); err != nil { t.Fatalf("unexpected error: %v", err) } 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) } } func TestQueryRowBytesInvalidatedByNext_bytesIntoRawBytes(t *testing.T) { t.Parallel() replace := []byte(invalid) rows := NewRows([]string{"raw"}). AddRow([]byte(`one binary value with some text!`)). AddRow([]byte(`two binary value with even more text than the first one`)) scan := func(rs *sql.Rows) ([]byte, error) { var raw sql.RawBytes return raw, rs.Scan(&raw) } want := []struct { Initial []byte Replaced []byte }{ {Initial: []byte(`one binary value with some text!`), Replaced: replace[:len(replace)-7]}, {Initial: []byte(`two binary value with even more text than the first one`), Replaced: bytes.Join([][]byte{replace, replace[:len(replace)-23]}, nil)}, } queryRowBytesInvalidatedByNext(t, rows, scan, want) } func TestQueryRowBytesNotInvalidatedByNext_bytesIntoBytes(t *testing.T) { t.Parallel() rows := NewRows([]string{"raw"}). AddRow([]byte(`one binary value with some text!`)). AddRow([]byte(`two binary value with even more text than the first one`)) scan := func(rs *sql.Rows) ([]byte, error) { var b []byte return b, rs.Scan(&b) } want := [][]byte{[]byte(`one binary value with some text!`), []byte(`two binary value with even more text than the first one`)} queryRowBytesNotInvalidatedByNext(t, rows, scan, want) } func TestQueryRowBytesNotInvalidatedByNext_stringIntoBytes(t *testing.T) { t.Parallel() rows := NewRows([]string{"raw"}). AddRow(`one binary value with some text!`). AddRow(`two binary value with even more text than the first one`) scan := func(rs *sql.Rows) ([]byte, error) { var b []byte return b, rs.Scan(&b) } want := [][]byte{[]byte(`one binary value with some text!`), []byte(`two binary value with even more text than the first one`)} queryRowBytesNotInvalidatedByNext(t, rows, scan, want) } func TestQueryRowBytesInvalidatedByClose_bytesIntoRawBytes(t *testing.T) { t.Parallel() replace := []byte(invalid) rows := NewRows([]string{"raw"}).AddRow([]byte(`one binary value with some text!`)) scan := func(rs *sql.Rows) ([]byte, error) { var raw sql.RawBytes return raw, rs.Scan(&raw) } want := struct { Initial []byte Replaced []byte }{ Initial: []byte(`one binary value with some text!`), Replaced: replace[:len(replace)-7], } queryRowBytesInvalidatedByClose(t, rows, scan, want) } func TestQueryRowBytesNotInvalidatedByClose_bytesIntoBytes(t *testing.T) { t.Parallel() rows := NewRows([]string{"raw"}).AddRow([]byte(`one binary value with some text!`)) scan := func(rs *sql.Rows) ([]byte, error) { var b []byte return b, rs.Scan(&b) } queryRowBytesNotInvalidatedByClose(t, rows, scan, []byte(`one binary value with some text!`)) } func TestQueryRowBytesNotInvalidatedByClose_stringIntoBytes(t *testing.T) { t.Parallel() rows := NewRows([]string{"raw"}).AddRow(`one binary value with some text!`) scan := func(rs *sql.Rows) ([]byte, error) { var b []byte return b, rs.Scan(&b) } queryRowBytesNotInvalidatedByClose(t, rows, scan, []byte(`one binary value with some text!`)) } func TestRowsScanError(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() r := NewRows([]string{"col1", "col2"}).AddRow("one", "two").AddRow("one", nil) mock.ExpectQuery("SELECT").WillReturnRows(r) rs, err := db.Query("SELECT") if err != nil { t.Fatalf("unexpected error: %s", err) } defer rs.Close() var one, two string if !rs.Next() || rs.Err() != nil || rs.Scan(&one, &two) != nil { t.Fatal("unexpected error on first row scan") } if !rs.Next() || rs.Err() != nil { t.Fatal("unexpected error on second row read") } err = rs.Scan(&one, &two) if err == nil { t.Fatal("expected an error for scan, but got none") } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestCSVRowParser(t *testing.T) { t.Parallel() rs := NewRows([]string{"col1", "col2", "col3"}).FromCSVString("a,NULL,NULL") 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(rs) rw, err := db.Query("SELECT") if err != nil { t.Fatalf("unexpected error: %s", err) } defer rw.Close() var col1 string var col2 []byte var col3 *string rw.Next() if err = rw.Scan(&col1, &col2, &col3); err != nil { t.Fatalf("unexpected error: %s", err) } if col1 != "a" { t.Fatalf("expected col1 to be 'a', but got [%T]:%+v", col1, col1) } if col2 != nil { t.Fatalf("expected col2 to be nil, but got [%T]:%+v", col2, col2) } if col3 != nil { t.Fatalf("expected col3 to be nil, but got [%T]:%+v", col3, col3) } } func TestCSVParserInvalidInput(t *testing.T) { defer func() { recover() }() _ = NewRows([]string{"col1", "col2"}).FromCSVString("a,\"NULL\"\"") // shouldn't reach here t.Error("expected panic from parsing invalid CSV") } func TestWrongNumberOfValues(t *testing.T) { // Open new mock database db, mock, err := New() if err != nil { fmt.Println("error creating mock database") return } defer db.Close() defer func() { recover() }() mock.ExpectQuery("SELECT ID FROM TABLE").WithArgs(101).WillReturnRows(NewRows([]string{"ID"}).AddRow(101, "Hello")) db.Query("SELECT ID FROM TABLE", 101) // shouldn't reach here t.Error("expected panic from query") } func TestEmptyRowSets(t *testing.T) { rs1 := NewRows([]string{"a"}).AddRow("a") rs2 := NewRows([]string{"b"}) rs3 := NewRows([]string{"c"}) set1 := &rowSets{sets: []*Rows{rs1, rs2}} set2 := &rowSets{sets: []*Rows{rs3, rs2}} set3 := &rowSets{sets: []*Rows{rs2}} if set1.empty() { t.Fatalf("expected rowset 1, not to be empty, but it was") } if !set2.empty() { t.Fatalf("expected rowset 2, to be empty, but it was not") } if !set3.empty() { t.Fatalf("expected rowset 3, to be empty, but it was not") } } func queryRowBytesInvalidatedByNext(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want []struct { Initial []byte Replaced []byte }) { 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(rows) rs, err := db.Query("SELECT") if err != nil { t.Fatalf("failed to query rows: %s", err) } if !rs.Next() || rs.Err() != nil { t.Fatal("unexpected error on first row retrieval") } var count int for i := 0; ; i++ { count++ b, err := scan(rs) if err != nil { t.Fatalf("unexpected error scanning row: %s", err) } if exp := want[i].Initial; !bytes.Equal(b, exp) { t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b)) } next := rs.Next() if exp := want[i].Replaced; !bytes.Equal(b, exp) { t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b)) } if !next { break } } if err := rs.Err(); err != nil { t.Fatalf("row iteration failed: %s", err) } if exp := len(want); count != exp { t.Fatalf("incorrect number of rows exp: %d, but got %d", exp, count) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func queryRowBytesNotInvalidatedByNext(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want [][]byte) { 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(rows) rs, err := db.Query("SELECT") if err != nil { t.Fatalf("failed to query rows: %s", err) } if !rs.Next() || rs.Err() != nil { t.Fatal("unexpected error on first row retrieval") } var count int for i := 0; ; i++ { count++ b, err := scan(rs) if err != nil { t.Fatalf("unexpected error scanning row: %s", err) } if exp := want[i]; !bytes.Equal(b, exp) { t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b)) } next := rs.Next() if exp := want[i]; !bytes.Equal(b, exp) { t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b)) } if !next { break } } if err := rs.Err(); err != nil { t.Fatalf("row iteration failed: %s", err) } if exp := len(want); count != exp { t.Fatalf("incorrect number of rows exp: %d, but got %d", exp, count) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func queryRowBytesInvalidatedByClose(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want struct { Initial []byte Replaced []byte }) { 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(rows) rs, err := db.Query("SELECT") if err != nil { t.Fatalf("failed to query rows: %s", err) } if !rs.Next() || rs.Err() != nil { t.Fatal("unexpected error on first row retrieval") } b, err := scan(rs) if err != nil { t.Fatalf("unexpected error scanning row: %s", err) } if !bytes.Equal(b, want.Initial) { t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", want.Initial, len(want.Initial), b, b, len(b)) } if err := rs.Close(); err != nil { t.Fatalf("unexpected error closing rows: %s", err) } if !bytes.Equal(b, want.Replaced) { t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", want.Replaced, len(want.Replaced), b, b, len(b)) } if err := rs.Err(); err != nil { t.Fatalf("row iteration failed: %s", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func queryRowBytesNotInvalidatedByClose(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want []byte) { 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(rows) rs, err := db.Query("SELECT") if err != nil { t.Fatalf("failed to query rows: %s", err) } if !rs.Next() || rs.Err() != nil { t.Fatal("unexpected error on first row retrieval") } b, err := scan(rs) if err != nil { t.Fatalf("unexpected error scanning row: %s", err) } if !bytes.Equal(b, want) { t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", want, len(want), b, b, len(b)) } if err := rs.Close(); err != nil { t.Fatalf("unexpected error closing rows: %s", err) } if !bytes.Equal(b, want) { t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", want, len(want), b, b, len(b)) } if err := rs.Err(); err != nil { t.Fatalf("row iteration failed: %s", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestAddRows(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() values := [][]driver.Value{ { 1, "John", }, { 2, "Jane", }, { 3, "Peter", }, { 4, "Emily", }, } rows := NewRows([]string{"id", "name"}).AddRows(values...) mock.ExpectQuery("SELECT").WillReturnRows(rows).RowsWillBeClosed() rs, _ := db.Query("SELECT") defer rs.Close() for rs.Next() { var id int var name string rs.Scan(&id, &name) fmt.Println("scanned id:", id, "and name:", name) } if rs.Err() != nil { fmt.Println("got rows error:", rs.Err()) } // Output: scanned id: 1 and title: John // scanned id: 2 and title: Jane // scanned id: 3 and title: Peter // scanned id: 4 and title: Emily } func TestAddRowExpectPanic(t *testing.T) { t.Parallel() const expectedPanic = "Expected number of values to match number of columns: expected 1, actual 2" values := []driver.Value{ "John", "Jane", } defer func() { if r := recover(); r != nil { if r != expectedPanic { t.Fatalf("panic message did not match expected: expected '%s', actual '%s'", r, expectedPanic) } return } t.Fatalf("expected panic: %s", expectedPanic) }() rows := NewRows([]string{"id", "name"}) // Note missing spread "..." rows.AddRow(values) } func ExampleRows_AddRows() { db, mock, err := New() if err != nil { fmt.Println("failed to open sqlmock database:", err) } defer db.Close() values := [][]driver.Value{ { 1, "one", }, { 2, "two", }, } rows := NewRows([]string{"id", "title"}).AddRows(values...) 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 }