Merge pull request #208 from zevst/master

migration from custom namedValue to driver.NamedValue
diff --git a/.gitignore b/.gitignore
index e4001c0..0e5426a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 /examples/blog/blog
 /examples/orders/orders
 /examples/basic/basic
+.idea/
diff --git a/expectations.go b/expectations.go
index ae2a47f..5c82c7b 100644
--- a/expectations.go
+++ b/expectations.go
@@ -339,21 +339,6 @@
 	args      []driver.Value
 }
 
-func (e *queryBasedExpectation) attemptArgMatch(args []namedValue) (err error) {
-	// catch panic
-	defer func() {
-		if e := recover(); e != nil {
-			_, ok := e.(error)
-			if !ok {
-				err = fmt.Errorf(e.(string))
-			}
-		}
-	}()
-
-	err = e.argsMatches(args)
-	return
-}
-
 // ExpectedPing is used to manage *sql.DB.Ping expectations.
 // Returned by *Sqlmock.ExpectPing.
 type ExpectedPing struct {
diff --git a/expectations_before_go18.go b/expectations_before_go18.go
index e368e04..f6e7b4e 100644
--- a/expectations_before_go18.go
+++ b/expectations_before_go18.go
@@ -50,3 +50,18 @@
 	}
 	return nil
 }
+
+func (e *queryBasedExpectation) attemptArgMatch(args []namedValue) (err error) {
+	// catch panic
+	defer func() {
+		if e := recover(); e != nil {
+			_, ok := e.(error)
+			if !ok {
+				err = fmt.Errorf(e.(string))
+			}
+		}
+	}()
+
+	err = e.argsMatches(args)
+	return
+}
diff --git a/expectations_before_go18_test.go b/expectations_before_go18_test.go
new file mode 100644
index 0000000..897ebff
--- /dev/null
+++ b/expectations_before_go18_test.go
@@ -0,0 +1,118 @@
+// +build !go1.8
+
+package sqlmock
+
+import (
+	"database/sql/driver"
+	"testing"
+	"time"
+)
+
+func TestQueryExpectationArgComparison(t *testing.T) {
+	e := &queryBasedExpectation{converter: driver.DefaultParameterConverter}
+	against := []namedValue{{Value: int64(5), Ordinal: 1}}
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should match, since the no expectation was set, but got err: %s", err)
+	}
+
+	e.args = []driver.Value{5, "str"}
+
+	against = []namedValue{{Value: int64(5), Ordinal: 1}}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since the size is not the same")
+	}
+
+	against = []namedValue{
+		{Value: int64(3), Ordinal: 1},
+		{Value: "str", Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since the first argument (int value) is different")
+	}
+
+	against = []namedValue{
+		{Value: int64(5), Ordinal: 1},
+		{Value: "st", Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since the second argument (string value) is different")
+	}
+
+	against = []namedValue{
+		{Value: int64(5), Ordinal: 1},
+		{Value: "str", Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should match, but it did not: %s", err)
+	}
+
+	const longForm = "Jan 2, 2006 at 3:04pm (MST)"
+	tm, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)")
+	e.args = []driver.Value{5, tm}
+
+	against = []namedValue{
+		{Value: int64(5), Ordinal: 1},
+		{Value: tm, Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Error("arguments should match, but it did not")
+	}
+
+	e.args = []driver.Value{5, AnyArg()}
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should match, but it did not: %s", err)
+	}
+}
+
+func TestQueryExpectationArgComparisonBool(t *testing.T) {
+	var e *queryBasedExpectation
+
+	e = &queryBasedExpectation{args: []driver.Value{true}, converter: driver.DefaultParameterConverter}
+	against := []namedValue{
+		{Value: true, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Error("arguments should match, since arguments are the same")
+	}
+
+	e = &queryBasedExpectation{args: []driver.Value{false}, converter: driver.DefaultParameterConverter}
+	against = []namedValue{
+		{Value: false, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Error("arguments should match, since argument are the same")
+	}
+
+	e = &queryBasedExpectation{args: []driver.Value{true}, converter: driver.DefaultParameterConverter}
+	against = []namedValue{
+		{Value: false, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since argument is different")
+	}
+
+	e = &queryBasedExpectation{args: []driver.Value{false}, converter: driver.DefaultParameterConverter}
+	against = []namedValue{
+		{Value: true, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since argument is different")
+	}
+}
+
+type panicConverter struct {
+}
+
+func (s panicConverter) ConvertValue(v interface{}) (driver.Value, error) {
+	panic(v)
+}
+
+func Test_queryBasedExpectation_attemptArgMatch(t *testing.T) {
+	e := &queryBasedExpectation{converter: new(panicConverter), args: []driver.Value{"test"}}
+	values := []namedValue{
+		{Ordinal: 1, Name: "test", Value: "test"},
+	}
+	if err := e.attemptArgMatch(values); err == nil {
+		t.Errorf("error expected")
+	}
+}
diff --git a/expectations_go18.go b/expectations_go18.go
index 6ee8adf..172bb6c 100644
--- a/expectations_go18.go
+++ b/expectations_go18.go
@@ -4,6 +4,7 @@
 
 import (
 	"database/sql"
+	"database/sql/driver"
 	"fmt"
 	"reflect"
 )
@@ -19,7 +20,7 @@
 	return e
 }
 
-func (e *queryBasedExpectation) argsMatches(args []namedValue) error {
+func (e *queryBasedExpectation) argsMatches(args []driver.NamedValue) error {
 	if nil == e.args {
 		return nil
 	}
@@ -59,3 +60,18 @@
 	}
 	return nil
 }
+
+func (e *queryBasedExpectation) attemptArgMatch(args []driver.NamedValue) (err error) {
+	// catch panic
+	defer func() {
+		if e := recover(); e != nil {
+			_, ok := e.(error)
+			if !ok {
+				err = fmt.Errorf(e.(string))
+			}
+		}
+	}()
+
+	err = e.argsMatches(args)
+	return
+}
diff --git a/expectations_go18_test.go b/expectations_go18_test.go
index 2b85db3..1974721 100644
--- a/expectations_go18_test.go
+++ b/expectations_go18_test.go
@@ -6,11 +6,104 @@
 	"database/sql"
 	"database/sql/driver"
 	"testing"
+	"time"
 )
 
+func TestQueryExpectationArgComparison(t *testing.T) {
+	e := &queryBasedExpectation{converter: driver.DefaultParameterConverter}
+	against := []driver.NamedValue{{Value: int64(5), Ordinal: 1}}
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should match, since the no expectation was set, but got err: %s", err)
+	}
+
+	e.args = []driver.Value{5, "str"}
+
+	against = []driver.NamedValue{{Value: int64(5), Ordinal: 1}}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since the size is not the same")
+	}
+
+	against = []driver.NamedValue{
+		{Value: int64(3), Ordinal: 1},
+		{Value: "str", Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since the first argument (int value) is different")
+	}
+
+	against = []driver.NamedValue{
+		{Value: int64(5), Ordinal: 1},
+		{Value: "st", Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since the second argument (string value) is different")
+	}
+
+	against = []driver.NamedValue{
+		{Value: int64(5), Ordinal: 1},
+		{Value: "str", Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should match, but it did not: %s", err)
+	}
+
+	const longForm = "Jan 2, 2006 at 3:04pm (MST)"
+	tm, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)")
+	e.args = []driver.Value{5, tm}
+
+	against = []driver.NamedValue{
+		{Value: int64(5), Ordinal: 1},
+		{Value: tm, Ordinal: 2},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Error("arguments should match, but it did not")
+	}
+
+	e.args = []driver.Value{5, AnyArg()}
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should match, but it did not: %s", err)
+	}
+}
+
+func TestQueryExpectationArgComparisonBool(t *testing.T) {
+	var e *queryBasedExpectation
+
+	e = &queryBasedExpectation{args: []driver.Value{true}, converter: driver.DefaultParameterConverter}
+	against := []driver.NamedValue{
+		{Value: true, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Error("arguments should match, since arguments are the same")
+	}
+
+	e = &queryBasedExpectation{args: []driver.Value{false}, converter: driver.DefaultParameterConverter}
+	against = []driver.NamedValue{
+		{Value: false, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err != nil {
+		t.Error("arguments should match, since argument are the same")
+	}
+
+	e = &queryBasedExpectation{args: []driver.Value{true}, converter: driver.DefaultParameterConverter}
+	against = []driver.NamedValue{
+		{Value: false, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since argument is different")
+	}
+
+	e = &queryBasedExpectation{args: []driver.Value{false}, converter: driver.DefaultParameterConverter}
+	against = []driver.NamedValue{
+		{Value: true, Ordinal: 1},
+	}
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since argument is different")
+	}
+}
+
 func TestQueryExpectationNamedArgComparison(t *testing.T) {
 	e := &queryBasedExpectation{converter: driver.DefaultParameterConverter}
-	against := []namedValue{{Value: int64(5), Name: "id"}}
+	against := []driver.NamedValue{{Value: int64(5), Name: "id"}}
 	if err := e.argsMatches(against); err != nil {
 		t.Errorf("arguments should match, since the no expectation was set, but got err: %s", err)
 	}
@@ -24,7 +117,7 @@
 		t.Error("arguments should not match, since the size is not the same")
 	}
 
-	against = []namedValue{
+	against = []driver.NamedValue{
 		{Value: int64(5), Name: "id"},
 		{Value: "str", Name: "s"},
 	}
@@ -33,7 +126,7 @@
 		t.Errorf("arguments should have matched, but it did not: %v", err)
 	}
 
-	against = []namedValue{
+	against = []driver.NamedValue{
 		{Value: int64(5), Name: "id"},
 		{Value: "str", Name: "username"},
 	}
@@ -44,7 +137,7 @@
 
 	e.args = []driver.Value{int64(5), "str"}
 
-	against = []namedValue{
+	against = []driver.NamedValue{
 		{Value: int64(5), Ordinal: 0},
 		{Value: "str", Ordinal: 1},
 	}
@@ -53,7 +146,7 @@
 		t.Error("arguments matched, but it should have not due to wrong Ordinal position")
 	}
 
-	against = []namedValue{
+	against = []driver.NamedValue{
 		{Value: int64(5), Ordinal: 1},
 		{Value: "str", Ordinal: 2},
 	}
@@ -62,3 +155,20 @@
 		t.Errorf("arguments should have matched, but it did not: %v", err)
 	}
 }
+
+type panicConverter struct {
+}
+
+func (s panicConverter) ConvertValue(v interface{}) (driver.Value, error) {
+	panic(v)
+}
+
+func Test_queryBasedExpectation_attemptArgMatch(t *testing.T) {
+	e := &queryBasedExpectation{converter: new(panicConverter), args: []driver.Value{"test"}}
+	values := []driver.NamedValue{
+		{Ordinal: 1, Name: "test", Value: "test"},
+	}
+	if err := e.attemptArgMatch(values); err == nil {
+		t.Errorf("error expected")
+	}
+}
diff --git a/expectations_test.go b/expectations_test.go
index c6889c3..afda582 100644
--- a/expectations_test.go
+++ b/expectations_test.go
@@ -6,98 +6,20 @@
 	"fmt"
 	"reflect"
 	"testing"
-	"time"
 )
 
-func TestQueryExpectationArgComparison(t *testing.T) {
-	e := &queryBasedExpectation{converter: driver.DefaultParameterConverter}
-	against := []namedValue{{Value: int64(5), Ordinal: 1}}
-	if err := e.argsMatches(against); err != nil {
-		t.Errorf("arguments should match, since the no expectation was set, but got err: %s", err)
-	}
+type CustomConverter struct{}
 
-	e.args = []driver.Value{5, "str"}
-
-	against = []namedValue{{Value: int64(5), Ordinal: 1}}
-	if err := e.argsMatches(against); err == nil {
-		t.Error("arguments should not match, since the size is not the same")
-	}
-
-	against = []namedValue{
-		{Value: int64(3), Ordinal: 1},
-		{Value: "str", Ordinal: 2},
-	}
-	if err := e.argsMatches(against); err == nil {
-		t.Error("arguments should not match, since the first argument (int value) is different")
-	}
-
-	against = []namedValue{
-		{Value: int64(5), Ordinal: 1},
-		{Value: "st", Ordinal: 2},
-	}
-	if err := e.argsMatches(against); err == nil {
-		t.Error("arguments should not match, since the second argument (string value) is different")
-	}
-
-	against = []namedValue{
-		{Value: int64(5), Ordinal: 1},
-		{Value: "str", Ordinal: 2},
-	}
-	if err := e.argsMatches(against); err != nil {
-		t.Errorf("arguments should match, but it did not: %s", err)
-	}
-
-	const longForm = "Jan 2, 2006 at 3:04pm (MST)"
-	tm, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)")
-	e.args = []driver.Value{5, tm}
-
-	against = []namedValue{
-		{Value: int64(5), Ordinal: 1},
-		{Value: tm, Ordinal: 2},
-	}
-	if err := e.argsMatches(against); err != nil {
-		t.Error("arguments should match, but it did not")
-	}
-
-	e.args = []driver.Value{5, AnyArg()}
-	if err := e.argsMatches(against); err != nil {
-		t.Errorf("arguments should match, but it did not: %s", err)
-	}
-}
-
-func TestQueryExpectationArgComparisonBool(t *testing.T) {
-	var e *queryBasedExpectation
-
-	e = &queryBasedExpectation{args: []driver.Value{true}, converter: driver.DefaultParameterConverter}
-	against := []namedValue{
-		{Value: true, Ordinal: 1},
-	}
-	if err := e.argsMatches(against); err != nil {
-		t.Error("arguments should match, since arguments are the same")
-	}
-
-	e = &queryBasedExpectation{args: []driver.Value{false}, converter: driver.DefaultParameterConverter}
-	against = []namedValue{
-		{Value: false, Ordinal: 1},
-	}
-	if err := e.argsMatches(against); err != nil {
-		t.Error("arguments should match, since argument are the same")
-	}
-
-	e = &queryBasedExpectation{args: []driver.Value{true}, converter: driver.DefaultParameterConverter}
-	against = []namedValue{
-		{Value: false, Ordinal: 1},
-	}
-	if err := e.argsMatches(against); err == nil {
-		t.Error("arguments should not match, since argument is different")
-	}
-
-	e = &queryBasedExpectation{args: []driver.Value{false}, converter: driver.DefaultParameterConverter}
-	against = []namedValue{
-		{Value: true, Ordinal: 1},
-	}
-	if err := e.argsMatches(against); err == nil {
-		t.Error("arguments should not match, since argument is different")
+func (s CustomConverter) ConvertValue(v interface{}) (driver.Value, error) {
+	switch v.(type) {
+	case string:
+		return v.(string), nil
+	case []string:
+		return v.([]string), nil
+	case int:
+		return v.(int), nil
+	default:
+		return nil, errors.New(fmt.Sprintf("cannot convert %T with value %v", v, v))
 	}
 }
 
@@ -140,20 +62,6 @@
 	}
 }
 
-type CustomConverter struct{}
-
-func (s CustomConverter) ConvertValue(v interface{}) (driver.Value, error) {
-	switch v.(type) {
-	case string:
-		return v.(string), nil
-	case []string:
-		return v.([]string), nil
-	case int:
-		return v.(int), nil
-	default:
-		return nil, errors.New(fmt.Sprintf("cannot convert %T with value %v", v, v))
-	}
-}
 func TestCustomValueConverterQueryScan(t *testing.T) {
 	db, mock, _ := New(ValueConverterOption(CustomConverter{}))
 	query := `
diff --git a/sqlmock.go b/sqlmock.go
index 9431d0e..90f789b 100644
--- a/sqlmock.go
+++ b/sqlmock.go
@@ -265,88 +265,6 @@
 	return e
 }
 
-// Exec meets http://golang.org/pkg/database/sql/driver/#Execer
-func (c *sqlmock) Exec(query string, args []driver.Value) (driver.Result, error) {
-	namedArgs := make([]namedValue, len(args))
-	for i, v := range args {
-		namedArgs[i] = namedValue{
-			Ordinal: i + 1,
-			Value:   v,
-		}
-	}
-
-	ex, err := c.exec(query, namedArgs)
-	if ex != nil {
-		time.Sleep(ex.delay)
-	}
-	if err != nil {
-		return nil, err
-	}
-
-	return ex.result, nil
-}
-
-func (c *sqlmock) exec(query string, args []namedValue) (*ExpectedExec, error) {
-	var expected *ExpectedExec
-	var fulfilled int
-	var ok bool
-	for _, next := range c.expected {
-		next.Lock()
-		if next.fulfilled() {
-			next.Unlock()
-			fulfilled++
-			continue
-		}
-
-		if c.ordered {
-			if expected, ok = next.(*ExpectedExec); ok {
-				break
-			}
-			next.Unlock()
-			return nil, fmt.Errorf("call to ExecQuery '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
-		}
-		if exec, ok := next.(*ExpectedExec); ok {
-			if err := c.queryMatcher.Match(exec.expectSQL, query); err != nil {
-				next.Unlock()
-				continue
-			}
-
-			if err := exec.attemptArgMatch(args); err == nil {
-				expected = exec
-				break
-			}
-		}
-		next.Unlock()
-	}
-	if expected == nil {
-		msg := "call to ExecQuery '%s' with args %+v was not expected"
-		if fulfilled == len(c.expected) {
-			msg = "all expectations were already fulfilled, " + msg
-		}
-		return nil, fmt.Errorf(msg, query, args)
-	}
-	defer expected.Unlock()
-
-	if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
-		return nil, fmt.Errorf("ExecQuery: %v", err)
-	}
-
-	if err := expected.argsMatches(args); err != nil {
-		return nil, fmt.Errorf("ExecQuery '%s', arguments do not match: %s", query, err)
-	}
-
-	expected.triggered = true
-	if expected.err != nil {
-		return expected, expected.err // mocked to return error
-	}
-
-	if expected.result == nil {
-		return nil, fmt.Errorf("ExecQuery '%s' with args %+v, must return a database/sql/driver.Result, but it was not set for expectation %T as %+v", query, args, expected, expected)
-	}
-
-	return expected, nil
-}
-
 func (c *sqlmock) ExpectExec(expectedSQL string) *ExpectedExec {
 	e := &ExpectedExec{}
 	e.expectSQL = expectedSQL
@@ -421,94 +339,6 @@
 	return e
 }
 
-type namedValue struct {
-	Name    string
-	Ordinal int
-	Value   driver.Value
-}
-
-// Query meets http://golang.org/pkg/database/sql/driver/#Queryer
-func (c *sqlmock) Query(query string, args []driver.Value) (driver.Rows, error) {
-	namedArgs := make([]namedValue, len(args))
-	for i, v := range args {
-		namedArgs[i] = namedValue{
-			Ordinal: i + 1,
-			Value:   v,
-		}
-	}
-
-	ex, err := c.query(query, namedArgs)
-	if ex != nil {
-		time.Sleep(ex.delay)
-	}
-	if err != nil {
-		return nil, err
-	}
-
-	return ex.rows, nil
-}
-
-func (c *sqlmock) query(query string, args []namedValue) (*ExpectedQuery, error) {
-	var expected *ExpectedQuery
-	var fulfilled int
-	var ok bool
-	for _, next := range c.expected {
-		next.Lock()
-		if next.fulfilled() {
-			next.Unlock()
-			fulfilled++
-			continue
-		}
-
-		if c.ordered {
-			if expected, ok = next.(*ExpectedQuery); ok {
-				break
-			}
-			next.Unlock()
-			return nil, fmt.Errorf("call to Query '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
-		}
-		if qr, ok := next.(*ExpectedQuery); ok {
-			if err := c.queryMatcher.Match(qr.expectSQL, query); err != nil {
-				next.Unlock()
-				continue
-			}
-			if err := qr.attemptArgMatch(args); err == nil {
-				expected = qr
-				break
-			}
-		}
-		next.Unlock()
-	}
-
-	if expected == nil {
-		msg := "call to Query '%s' with args %+v was not expected"
-		if fulfilled == len(c.expected) {
-			msg = "all expectations were already fulfilled, " + msg
-		}
-		return nil, fmt.Errorf(msg, query, args)
-	}
-
-	defer expected.Unlock()
-
-	if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
-		return nil, fmt.Errorf("Query: %v", err)
-	}
-
-	if err := expected.argsMatches(args); err != nil {
-		return nil, fmt.Errorf("Query '%s', arguments do not match: %s", query, err)
-	}
-
-	expected.triggered = true
-	if expected.err != nil {
-		return expected, expected.err // mocked to return error
-	}
-
-	if expected.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, expected, expected)
-	}
-	return expected, nil
-}
-
 func (c *sqlmock) ExpectQuery(expectedSQL string) *ExpectedQuery {
 	e := &ExpectedQuery{}
 	e.expectSQL = expectedSQL
diff --git a/sqlmock_before_go18.go b/sqlmock_before_go18.go
index 88b7aa0..1a5b63a 100644
--- a/sqlmock_before_go18.go
+++ b/sqlmock_before_go18.go
@@ -2,9 +2,184 @@
 
 package sqlmock
 
-import "log"
+import (
+	"database/sql/driver"
+	"fmt"
+	"log"
+	"time"
+)
+
+type namedValue struct {
+	Name    string
+	Ordinal int
+	Value   driver.Value
+}
 
 func (c *sqlmock) ExpectPing() *ExpectedPing {
 	log.Println("ExpectPing has no effect on Go 1.7 or below")
 	return &ExpectedPing{}
 }
+
+// Query meets http://golang.org/pkg/database/sql/driver/#Queryer
+func (c *sqlmock) Query(query string, args []driver.Value) (driver.Rows, error) {
+	namedArgs := make([]namedValue, len(args))
+	for i, v := range args {
+		namedArgs[i] = namedValue{
+			Ordinal: i + 1,
+			Value:   v,
+		}
+	}
+
+	ex, err := c.query(query, namedArgs)
+	if ex != nil {
+		time.Sleep(ex.delay)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return ex.rows, nil
+}
+
+func (c *sqlmock) query(query string, args []namedValue) (*ExpectedQuery, error) {
+	var expected *ExpectedQuery
+	var fulfilled int
+	var ok bool
+	for _, next := range c.expected {
+		next.Lock()
+		if next.fulfilled() {
+			next.Unlock()
+			fulfilled++
+			continue
+		}
+
+		if c.ordered {
+			if expected, ok = next.(*ExpectedQuery); ok {
+				break
+			}
+			next.Unlock()
+			return nil, fmt.Errorf("call to Query '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
+		}
+		if qr, ok := next.(*ExpectedQuery); ok {
+			if err := c.queryMatcher.Match(qr.expectSQL, query); err != nil {
+				next.Unlock()
+				continue
+			}
+			if err := qr.attemptArgMatch(args); err == nil {
+				expected = qr
+				break
+			}
+		}
+		next.Unlock()
+	}
+
+	if expected == nil {
+		msg := "call to Query '%s' with args %+v was not expected"
+		if fulfilled == len(c.expected) {
+			msg = "all expectations were already fulfilled, " + msg
+		}
+		return nil, fmt.Errorf(msg, query, args)
+	}
+
+	defer expected.Unlock()
+
+	if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
+		return nil, fmt.Errorf("Query: %v", err)
+	}
+
+	if err := expected.argsMatches(args); err != nil {
+		return nil, fmt.Errorf("Query '%s', arguments do not match: %s", query, err)
+	}
+
+	expected.triggered = true
+	if expected.err != nil {
+		return expected, expected.err // mocked to return error
+	}
+
+	if expected.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, expected, expected)
+	}
+	return expected, nil
+}
+
+// Exec meets http://golang.org/pkg/database/sql/driver/#Execer
+func (c *sqlmock) Exec(query string, args []driver.Value) (driver.Result, error) {
+	namedArgs := make([]namedValue, len(args))
+	for i, v := range args {
+		namedArgs[i] = namedValue{
+			Ordinal: i + 1,
+			Value:   v,
+		}
+	}
+
+	ex, err := c.exec(query, namedArgs)
+	if ex != nil {
+		time.Sleep(ex.delay)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return ex.result, nil
+}
+
+func (c *sqlmock) exec(query string, args []namedValue) (*ExpectedExec, error) {
+	var expected *ExpectedExec
+	var fulfilled int
+	var ok bool
+	for _, next := range c.expected {
+		next.Lock()
+		if next.fulfilled() {
+			next.Unlock()
+			fulfilled++
+			continue
+		}
+
+		if c.ordered {
+			if expected, ok = next.(*ExpectedExec); ok {
+				break
+			}
+			next.Unlock()
+			return nil, fmt.Errorf("call to ExecQuery '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
+		}
+		if exec, ok := next.(*ExpectedExec); ok {
+			if err := c.queryMatcher.Match(exec.expectSQL, query); err != nil {
+				next.Unlock()
+				continue
+			}
+
+			if err := exec.attemptArgMatch(args); err == nil {
+				expected = exec
+				break
+			}
+		}
+		next.Unlock()
+	}
+	if expected == nil {
+		msg := "call to ExecQuery '%s' with args %+v was not expected"
+		if fulfilled == len(c.expected) {
+			msg = "all expectations were already fulfilled, " + msg
+		}
+		return nil, fmt.Errorf(msg, query, args)
+	}
+	defer expected.Unlock()
+
+	if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
+		return nil, fmt.Errorf("ExecQuery: %v", err)
+	}
+
+	if err := expected.argsMatches(args); err != nil {
+		return nil, fmt.Errorf("ExecQuery '%s', arguments do not match: %s", query, err)
+	}
+
+	expected.triggered = true
+	if expected.err != nil {
+		return expected, expected.err // mocked to return error
+	}
+
+	if expected.result == nil {
+		return nil, fmt.Errorf("ExecQuery '%s' with args %+v, must return a database/sql/driver.Result, but it was not set for expectation %T as %+v", query, args, expected, expected)
+	}
+
+	return expected, nil
+}
diff --git a/sqlmock_go18.go b/sqlmock_go18.go
index 43fbb5d..dc37b18 100644
--- a/sqlmock_go18.go
+++ b/sqlmock_go18.go
@@ -17,12 +17,7 @@
 
 // Implement the "QueryerContext" interface
 func (c *sqlmock) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
-	namedArgs := make([]namedValue, len(args))
-	for i, nv := range args {
-		namedArgs[i] = namedValue(nv)
-	}
-
-	ex, err := c.query(query, namedArgs)
+	ex, err := c.query(query, args)
 	if ex != nil {
 		select {
 		case <-time.After(ex.delay):
@@ -40,12 +35,7 @@
 
 // Implement the "ExecerContext" interface
 func (c *sqlmock) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
-	namedArgs := make([]namedValue, len(args))
-	for i, nv := range args {
-		namedArgs[i] = namedValue(nv)
-	}
-
-	ex, err := c.exec(query, namedArgs)
+	ex, err := c.exec(query, args)
 	if ex != nil {
 		select {
 		case <-time.After(ex.delay):
@@ -170,4 +160,170 @@
 	return e
 }
 
+// Query meets http://golang.org/pkg/database/sql/driver/#Queryer
+// Deprecated: Drivers should implement QueryerContext instead.
+func (c *sqlmock) Query(query string, args []driver.Value) (driver.Rows, error) {
+	namedArgs := make([]driver.NamedValue, len(args))
+	for i, v := range args {
+		namedArgs[i] = driver.NamedValue{
+			Ordinal: i + 1,
+			Value:   v,
+		}
+	}
+
+	ex, err := c.query(query, namedArgs)
+	if ex != nil {
+		time.Sleep(ex.delay)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return ex.rows, nil
+}
+
+func (c *sqlmock) query(query string, args []driver.NamedValue) (*ExpectedQuery, error) {
+	var expected *ExpectedQuery
+	var fulfilled int
+	var ok bool
+	for _, next := range c.expected {
+		next.Lock()
+		if next.fulfilled() {
+			next.Unlock()
+			fulfilled++
+			continue
+		}
+
+		if c.ordered {
+			if expected, ok = next.(*ExpectedQuery); ok {
+				break
+			}
+			next.Unlock()
+			return nil, fmt.Errorf("call to Query '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
+		}
+		if qr, ok := next.(*ExpectedQuery); ok {
+			if err := c.queryMatcher.Match(qr.expectSQL, query); err != nil {
+				next.Unlock()
+				continue
+			}
+			if err := qr.attemptArgMatch(args); err == nil {
+				expected = qr
+				break
+			}
+		}
+		next.Unlock()
+	}
+
+	if expected == nil {
+		msg := "call to Query '%s' with args %+v was not expected"
+		if fulfilled == len(c.expected) {
+			msg = "all expectations were already fulfilled, " + msg
+		}
+		return nil, fmt.Errorf(msg, query, args)
+	}
+
+	defer expected.Unlock()
+
+	if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
+		return nil, fmt.Errorf("Query: %v", err)
+	}
+
+	if err := expected.argsMatches(args); err != nil {
+		return nil, fmt.Errorf("Query '%s', arguments do not match: %s", query, err)
+	}
+
+	expected.triggered = true
+	if expected.err != nil {
+		return expected, expected.err // mocked to return error
+	}
+
+	if expected.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, expected, expected)
+	}
+	return expected, nil
+}
+
+// Exec meets http://golang.org/pkg/database/sql/driver/#Execer
+// Deprecated: Drivers should implement ExecerContext instead.
+func (c *sqlmock) Exec(query string, args []driver.Value) (driver.Result, error) {
+	namedArgs := make([]driver.NamedValue, len(args))
+	for i, v := range args {
+		namedArgs[i] = driver.NamedValue{
+			Ordinal: i + 1,
+			Value:   v,
+		}
+	}
+
+	ex, err := c.exec(query, namedArgs)
+	if ex != nil {
+		time.Sleep(ex.delay)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return ex.result, nil
+}
+
+func (c *sqlmock) exec(query string, args []driver.NamedValue) (*ExpectedExec, error) {
+	var expected *ExpectedExec
+	var fulfilled int
+	var ok bool
+	for _, next := range c.expected {
+		next.Lock()
+		if next.fulfilled() {
+			next.Unlock()
+			fulfilled++
+			continue
+		}
+
+		if c.ordered {
+			if expected, ok = next.(*ExpectedExec); ok {
+				break
+			}
+			next.Unlock()
+			return nil, fmt.Errorf("call to ExecQuery '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
+		}
+		if exec, ok := next.(*ExpectedExec); ok {
+			if err := c.queryMatcher.Match(exec.expectSQL, query); err != nil {
+				next.Unlock()
+				continue
+			}
+
+			if err := exec.attemptArgMatch(args); err == nil {
+				expected = exec
+				break
+			}
+		}
+		next.Unlock()
+	}
+	if expected == nil {
+		msg := "call to ExecQuery '%s' with args %+v was not expected"
+		if fulfilled == len(c.expected) {
+			msg = "all expectations were already fulfilled, " + msg
+		}
+		return nil, fmt.Errorf(msg, query, args)
+	}
+	defer expected.Unlock()
+
+	if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
+		return nil, fmt.Errorf("ExecQuery: %v", err)
+	}
+
+	if err := expected.argsMatches(args); err != nil {
+		return nil, fmt.Errorf("ExecQuery '%s', arguments do not match: %s", query, err)
+	}
+
+	expected.triggered = true
+	if expected.err != nil {
+		return expected, expected.err // mocked to return error
+	}
+
+	if expected.result == nil {
+		return nil, fmt.Errorf("ExecQuery '%s' with args %+v, must return a database/sql/driver.Result, but it was not set for expectation %T as %+v", query, args, expected, expected)
+	}
+
+	return expected, nil
+}
+
 // @TODO maybe add ExpectedBegin.WithOptions(driver.TxOptions)
diff --git a/sqlmock_go19_test.go b/sqlmock_go19_test.go
index 6c69559..910d704 100644
--- a/sqlmock_go19_test.go
+++ b/sqlmock_go19_test.go
@@ -3,6 +3,8 @@
 package sqlmock
 
 import (
+	"database/sql"
+	"database/sql/driver"
 	"errors"
 	"testing"
 )
@@ -37,3 +39,32 @@
 		t.Fatalf("unexpected result: %v", err)
 	}
 }
+
+func Test_sqlmock_CheckNamedValue(t *testing.T) {
+	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()
+	tests := []struct {
+		name    string
+		arg     *driver.NamedValue
+		wantErr bool
+	}{
+		{
+			arg:     &driver.NamedValue{Name: "test", Value: "test"},
+			wantErr: false,
+		},
+		{
+			arg:     &driver.NamedValue{Name: "test", Value: sql.Out{}},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := mock.(*sqlmock).CheckNamedValue(tt.arg); (err != nil) != tt.wantErr {
+				t.Errorf("CheckNamedValue() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/sqlmock_test.go b/sqlmock_test.go
index 522ea42..ee6b516 100644
--- a/sqlmock_test.go
+++ b/sqlmock_test.go
@@ -2,8 +2,10 @@
 
 import (
 	"database/sql"
+	"database/sql/driver"
 	"errors"
 	"fmt"
+	"reflect"
 	"strconv"
 	"sync"
 	"testing"
@@ -1217,3 +1219,124 @@
 		return nil, fmt.Errorf("query timed out after %v", t)
 	}
 }
+
+func Test_sqlmock_Prepare_and_Exec(t *testing.T) {
+	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()
+	query := "SELECT name, email FROM users WHERE name = ?"
+
+	mock.ExpectPrepare("SELECT (.+) FROM users WHERE (.+)")
+	expected := NewResult(1, 1)
+	mock.ExpectExec("SELECT (.+) FROM users WHERE (.+)").
+		WillReturnResult(expected)
+	expectedRows := mock.NewRows([]string{"id", "name", "email"}).AddRow(1, "test", "test@example.com")
+	mock.ExpectQuery("SELECT (.+) FROM users WHERE (.+)").WillReturnRows(expectedRows)
+
+	got, err := mock.(*sqlmock).Prepare(query)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if got == nil {
+		t.Error("Prepare () stmt must not be nil")
+		return
+	}
+	result, err := got.Exec([]driver.Value{"test"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Errorf("Results are not equal. Expected: %v, Actual: %v", expected, result)
+		return
+	}
+	rows, err := got.Query([]driver.Value{"test"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	defer rows.Close()
+}
+
+type failArgument struct{}
+
+func (f failArgument) Match(_ driver.Value) bool {
+	return false
+}
+
+func Test_sqlmock_Exec(t *testing.T) {
+	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.ExpectBegin()
+	_, err = mock.(*sqlmock).Exec("", []driver.Value{})
+	if err == nil {
+		t.Errorf("error expected")
+		return
+	}
+
+	expected := NewResult(1, 1)
+	mock.ExpectExec("SELECT (.+) FROM users WHERE (.+)").
+		WillReturnResult(expected).
+		WithArgs("test")
+
+	matchErr := errors.New("matcher sqlmock.failArgument could not match 0 argument driver.NamedValue - {Name: Ordinal:1 Value:{}}")
+	mock.ExpectExec("SELECT (.+) FROM animals WHERE (.+)").
+		WillReturnError(matchErr).
+		WithArgs(failArgument{})
+
+	mock.ExpectExec("").WithArgs(failArgument{})
+
+	mock.(*sqlmock).expected = mock.(*sqlmock).expected[1:]
+	query := "SELECT name, email FROM users WHERE name = ?"
+	result, err := mock.(*sqlmock).Exec(query, []driver.Value{"test"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Errorf("Results are not equal. Expected: %v, Actual: %v", expected, result)
+		return
+	}
+
+	failQuery := "SELECT name, sex FROM animals WHERE sex = ?"
+	_, err = mock.(*sqlmock).Exec(failQuery, []driver.Value{failArgument{}})
+	if err == nil {
+		t.Errorf("error expected")
+		return
+	}
+	mock.(*sqlmock).ordered = false
+	_, err = mock.(*sqlmock).Exec("", []driver.Value{failArgument{}})
+	if err == nil {
+		t.Errorf("error expected")
+		return
+	}
+}
+
+func Test_sqlmock_Query(t *testing.T) {
+	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()
+	expectedRows := mock.NewRows([]string{"id", "name", "email"}).AddRow(1, "test", "test@example.com")
+	mock.ExpectQuery("SELECT (.+) FROM users WHERE (.+)").WillReturnRows(expectedRows)
+	query := "SELECT name, email FROM users WHERE name = ?"
+	rows, err := mock.(*sqlmock).Query(query, []driver.Value{"test"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	defer rows.Close()
+	_, err = mock.(*sqlmock).Query(query, []driver.Value{failArgument{}})
+	if err == nil {
+		t.Errorf("error expected")
+		return
+	}
+}
diff --git a/statement.go b/statement.go
index 570efd9..852b8f3 100644
--- a/statement.go
+++ b/statement.go
@@ -1,9 +1,5 @@
 package sqlmock
 
-import (
-	"database/sql/driver"
-)
-
 type statement struct {
 	conn  *sqlmock
 	ex    *ExpectedPrepare
@@ -18,11 +14,3 @@
 func (stmt *statement) NumInput() int {
 	return -1
 }
-
-func (stmt *statement) Exec(args []driver.Value) (driver.Result, error) {
-	return stmt.conn.Exec(stmt.query, args)
-}
-
-func (stmt *statement) Query(args []driver.Value) (driver.Rows, error) {
-	return stmt.conn.Query(stmt.query, args)
-}
diff --git a/statement_before_go18.go b/statement_before_go18.go
new file mode 100644
index 0000000..e2cac2b
--- /dev/null
+++ b/statement_before_go18.go
@@ -0,0 +1,17 @@
+// +build !go1.8
+
+package sqlmock
+
+import (
+	"database/sql/driver"
+)
+
+// Deprecated: Drivers should implement ExecerContext instead.
+func (stmt *statement) Exec(args []driver.Value) (driver.Result, error) {
+	return stmt.conn.Exec(stmt.query, args)
+}
+
+// Deprecated: Drivers should implement StmtQueryContext instead (or additionally).
+func (stmt *statement) Query(args []driver.Value) (driver.Rows, error) {
+	return stmt.conn.Query(stmt.query, args)
+}
diff --git a/statement_go18.go b/statement_go18.go
new file mode 100644
index 0000000..e083051
--- /dev/null
+++ b/statement_go18.go
@@ -0,0 +1,26 @@
+// +build go1.8
+
+package sqlmock
+
+import (
+	"context"
+	"database/sql/driver"
+)
+
+// Deprecated: Drivers should implement ExecerContext instead.
+func (stmt *statement) Exec(args []driver.Value) (driver.Result, error) {
+	return stmt.conn.ExecContext(context.Background(), stmt.query, convertValueToNamedValue(args))
+}
+
+// Deprecated: Drivers should implement StmtQueryContext instead (or additionally).
+func (stmt *statement) Query(args []driver.Value) (driver.Rows, error) {
+	return stmt.conn.QueryContext(context.Background(), stmt.query, convertValueToNamedValue(args))
+}
+
+func convertValueToNamedValue(args []driver.Value) []driver.NamedValue {
+	namedArgs := make([]driver.NamedValue, len(args))
+	for i, v := range args {
+		namedArgs[i] = driver.NamedValue{Ordinal: i + 1, Value: v}
+	}
+	return namedArgs
+}