go-sqlmock icon indicating copy to clipboard operation
go-sqlmock copied to clipboard

sql.Out values are not set by mock database

Open jwenz723 opened this issue 5 years ago • 1 comments
trafficstars

I have a query that I run that has a few sql.Out parameters. Using go-sqlmock, it doesn't appear to be possible to have values injected into the Dest field of a sql.Out parameter when the query is executed.

#174 added some functionality to make some sql.Out testing possible, but it doesn't appear to be possible to fully test these types of queries.

For example, here is my query I have in my code:

func (r mgRepo) ExecuteQuery(ctx context.Context, area model.Area, cluster string, requestCount int64) (*MyGlobalData, error) {
	var AvailableRangeStart int64
	var IDBlockSize int64
	_, err := r.db.QueryContext(
		ctx,
		r.sProc,
		sql.Named("TableName", area.String()),
		sql.Named("MinBlockSize", requestCount),
		sql.Named("AvailableRangeStart", sql.Out{Dest: &AvailableRangeStart}),
		sql.Named("IDBlockSize", sql.Out{Dest: &IDBlockSize}),
		sql.Named("RequestingServer", cluster),
	)
	if err != nil {
		return nil, err
	}
	if AvailableRangeStart == 0 || IDBlockSize == 0 {
		return nil, errors.New("failed to retrieve block")
	}
	return &MyGlobalData{
		RangeStart: AvailableRangeStart,
		RangeEnd:   AvailableRangeStart + IDBlockSize - 1,
	}, nil
}

This function validates that the AvailableRangeStart and IDBlockSize sql.Out parameters have been assigned (checks that they don't have the default value of 0), which means I can't run this function using a mock database without the errors.New("failed to retrieve block") error being returned.

Here is my test:

type CustomConverter struct{}

func (s CustomConverter) ConvertValue(v interface{}) (driver.Value, error) {
	if val, ok1 := v.(driver.NamedValue); ok1 {
		return val.Value, nil
	}

	if val, ok := v.(sql.Out); ok {
		res := val.Dest.(*int64)
		return *res, nil
	}
	return v, nil
}

func TestMgRepo_ExecuteQuery(t *testing.T) {
	converter := sqlmock.ValueConverterOption(CustomConverter{})
	db, mock, err := sqlmock.New(converter)
	if err != nil {
		t.Errorf("failed to create sql mock")
	}

	a := int64(100)
	i := int64(10)
	
	mock.ExpectQuery("test").WithArgs(
		sql.Named("TableName", "Contact_log"),
		sql.Named("MinBlockSize", int64(10)),
		sql.Named("AvailableRangeStart", sql.Out{Dest: &a}),
		sql.Named("IDBlockSize", sql.Out{Dest: &i}),
		sql.Named("RequestingServer", "test"),
	).WillReturnRows(nil)
	mgRepo := mgRepo{
		db:    db,
		sProc: "test",
	}
	_, err = mgRepo.ExecuteQuery(context.Background(), model.Contact_log, "test", int64(10))
	assert.Nil(t, err)
}

Here is the test execution output:

=== RUN   TestMgRepo_ExecuteQuery
--- FAIL: TestMgRepo_ExecuteQuery (0.00s)
    myGlobal_test.go:164: 
        	Error Trace:	myGlobal_test.go:164
        	Error:      	Expected nil, but got: &errors.errorString{s:"Query 'test', arguments do not match: argument 2 expected [int64 - 100] does not match actual [int64 - 0]"}
        	Test:       	TestMgRepo_ExecuteQuery
FAIL

jwenz723 avatar Dec 06 '19 21:12 jwenz723

Hi, maybe you can try and investigate what change needs to be added and submit a PR.

l3pp4rd avatar Dec 09 '19 07:12 l3pp4rd