Skip to content

golang sqlmock extend

foolmacky edited this page Nov 18, 2022 · 2 revisions

■go-sqlmock proc outparam対応

何をしたいのか?

  • mockに対して、期待するAction, 期待されるParam0がセットされる
  • db.Execが実行され、Param1が与えられる
  • 与えられたパラメータがチェックされるが無視される
  • param1の内容がparam0に置き換えられる
ExpectExec{
	queryBasedExpectation{
		commonExpectation{
			sync.Mutex
			triggered bool
			err       error
		}
	}
		expectSQL string
		converter driver.ValueConverter
		args      []driver.Value
	}
	result driver.Result
	delay  time.Duration
}

db, mock, err := New()

mock.ExceptExec("").WithArgs(param0).WithOutArgs(param1)
db.ExecOut("", param0)
	--param0の値がparam1に置換される
	db.Exec("", param0)
	param0 = param1

独自タイプの場合、driverにわたす前にエラーになってしまう

2020/07/25 01:24:51 error : sql: converting argument with name "out" type: unsupported type sqlmock.anyArgument, a struct
2020/07/25 01:11:43 error : sql: converting argument with name "out" type: unsupported type main.MockOut, a struct

expectは nil, execはsql.Outの場合

2020/07/25 02:01:52 error : ExecQuery 'insert into hoge', arguments do not match: argument 2 expected [<nil> - <nil>] does not match actual [sql.Out - {_Named_Fields_Required:{} Dest:<nil> In:false}]

expect , exec ともパラメータがsql.Outの場合
driverにはそのまま渡されるが、Match不能でエラーになる

2020/07/25 02:05:50 error : ExecQuery 'insert into hoge', arguments do not match: could not convert 2 argument sql.Out - {_Named_Fields_Required:{} Dest:10 In:false} to driver value: unsupported type sql.Out, a struct

expectをAnyArgにして、実際のexecをsql.Outで与えれば、マッチングはOKになる。
あとは内部でparamを強制的に書き換える

	args := []sql.NamedArg{
		{Name: "name", Value: "foolmacky"},
		{Name: "age", Value: 50},
		{Name: "out", Value: sqlmock.AnyArg()},
	}
	args0 := []sql.NamedArg{
		{Name: "name", Value: "foolmacky"},
		{Name: "age", Value: 50},
		{Name: "out", Value: sql.Out{Dest: 10}},
	}

このパターンはだめ

	args := []sql.NamedArg{
		{Name: "name", Value: "foolmacky"},
		{Name: "age", Value: 50},
		{Name: "out", Value: sql.Out{Dest: sqlmock.AnyArg()}},
	}
	args0 := []sql.NamedArg{
		{Name: "name", Value: "foolmacky"},
		{Name: "age", Value: 50},
		{Name: "out", Value: sql.Out{Dest: 10}},
	}

方向性

  • expectのパラメータはsql.OutをAnyArgに置換
  • argを複製してdriver
2020/07/25 02:49:39 error : ExecQuery 'insert into hoge', arguments do not match: matcher main.MockOut could not match 2 argument driver.NamedValue - {Name:out Ordinal:3 Value:{_Named_Fields_Required:{} Dest:10 In:false}}


	args := []sql.NamedArg{
		{Name: "name", Value: "foolmacky"},
		{Name: "age", Value: 50},
		{Name: "out", Value: MockOut{Out: sql.Out{Dest: 20}}},
		//		{Name: "out", Value: sql.Out{Dest: sqlmock.AnyArg()}},
	}
	args0 := []sql.NamedArg{
		{Name: "name", Value: "foolmacky"},
		{Name: "age", Value: 50},
		{Name: "out", Value: sql.Out{Dest: 10}},
	}

	// @TODO should we assert either all args are named or ordinal?
	for k, v := range args {
		// custom argument matcher
		matcher, ok := e.args[k].(Argument)
		if ok {
			if !matcher.Match(v.Value) {
				return fmt.Errorf("matcher %T could not match %d argument %T - %+v", matcher, k, args[k], args[k])
			}
			continue
		}
			//macher : expectで入れた値
			//v : 実際のexecで入ってくる値

Match関数がやるべきこと

  • trueをリターンする
  • Execで与えられたparameterをResultOutで与えられた値で置換する

vはexecで与えられた値
matcherはResultOutで与えられているparamをArgument(interface)型に変換したもの
それによってMatch関数がcallされる
どの値に変換すべきかは、予めmatcherは知っていることができる
変化させる値は、exec時に与えられるSql.OutのDest: *pointer の実体側
sql.OutのDestはpointerで格納されている

MockOutの自動生成

		{Name: "out", Value: MockOut{Dest: 20, In: true, Pmatch: true}},

Clone this wiki locally