DATA-DOG / go-sqlmock

Sql mock driver for golang to test database interactions
Other
6.09k stars 408 forks source link

Argument matching for queries using unpacked slices #331

Closed gkoscky closed 3 months ago

gkoscky commented 9 months ago

I have a sqlx.Query being called on a named query with multiple parameters. You can see a working example, but here's the abridged version:

type myStruct struct {
  ColumnA string   `db:"columnA"`
  ColumnB string   `db:"columnB"`
  ColumnC []string `db:"columnC"`
}

func runQuery(db *sqlx.DB, arguments myStruct) ([]myStruct, error) {
  namedQuery := "SELECT * FROM my_table WHERE columnA = :columnA AND columnB = :columnB AND columnC IN (:columnC)"

  query, args, _ := sqlx.Named(namedQuery, arguments)
  query, args, _ = sqlx.In(query, args...)
  query = db.Rebind(query)

  rows, err := db.Queryx(query, args...)
  if err != nil {
    return nil, fmt.Errorf("error querying: %s", err)
  }
  //…
}

And then a test case like so

  mockDb, mock, err := sqlmock.New()
  sqlxDB := sqlx.NewDb(mockDb, "sqlmock")

  toQuery := myStruct{
    ColumnA: "testA",
    ColumnB: "testB",
    ColumnC: []string{"testC", "testD"},
  }

  queryArgs := []string{"testA", "testB", "testC", "testD"}

  returnRows := mock.NewRows([]string{"columnA", "columnB"}).AddRow("returnA", "returnB")
  mock.ExpectQuery("SELECT \\* FROM my_table ").WithArgs(queryArgs).WillReturnRows(returnRows)

  _, err = runQuery(sqlxDB, toQuery)
  if err != nil {
    log.Println(err)
    return
  }

Which results in this error message:

Query 'SELECT * FROM my_table WHERE columnA = ? AND columnB = ? AND columnC IN (?, ?)',
  arguments do not match: expected 1, but got 4 arguments

The error makes sense, because I am passing queryArgs ([]string) to WithArgs, while the actual Queryx(query, args...) method gets an unpacked []interface{} as its arguments. But doing a similar AddRow(queryArgs...) results in:

cannot use queryArgs (variable of type []string) as []driver.Value value in argument to mock.ExpectQuery

Which leads me to my question: How can I pass an array to WithArgs that would match the unpacked arguments passed to Queryx?

I know I could hard code it, but in the non-simplified version of this code, the number of arguments is variable for each test case.

diegommm commented 3 months ago

Hi @gkoscky! That happens because AddRow has a variadic argument with element type driver.Value, which is much like having a []driver.Value as single argument, and you are passing a []string. To learn more, I recommend the following articles: