Mock time.Now()
I have a function with
func (r *repository) Update(ctx context.Context, book *models.Book) error {
now := time.Now()
_, err := r.db.ExecContext(ctx, UPDATE books set title = $1, description = $2, published_date = $3, image_url = $4, updated_at = $5 where book_id = $6, book.Title, book.Description,
book.PublishedDate, book.ImageURL, now, book.BookID)
if err != nil {
return err
}
return nil
}
that I tried to unit test with
func TestRepository_Update(t *testing.T) {
db, mock := NewMock()
repo := New(db)
mockBook := &models.Book{
BookID: 1,
Title: "test1",
PublishedDate: timeWant(),
ImageURL: null.String{
String: "https://example.com/image.png",
Valid: true,
},
Description: "test1",
}
mock.ExpectExec("UPDATE books set title").
WithArgs(mockBook.Title, mockBook.Description, mockBook.PublishedDate, mockBook.ImageURL.String, time.Now().String(), mockBook.BookID).
WillReturnResult(sqlmock.NewErrorResult(nil))
err := repo.Update(context.Background(), mockBook)
assert.NoError(t, err)
}
The fails obviously because the value of time.Now() in the test and in the implementation are different.
=== RUN TestRepository_Update
postgres_test.go:189:
Error Trace: postgres_test.go:189
Error: Received unexpected error:
ExecQuery 'UPDATE books set title = $1, description = $2, published_date = $3, image_url = $4, updated_at = $5 where book_id = $6', arguments do not match: argument 4 expected [string - 2021-05-26 22:07:58.847659183 +1000 AEST m=+0.001800825] does not match actual [time.Time - 2021-05-26 22:07:58.847710743 +1000 AEST m=+0.001852395]
Test: TestRepository_Update
--- FAIL: TestRepository_Update (0.00s)
How do you write unit a test when the implementation has a value that will change like time.Now() or somehow mock time.Now()
For reference, book struct
type Book struct {
BookID int64 `db:"book_id" json:"book_id"`
Title string `db:"title" json:"title"`
PublishedDate time.Time `db:"published_date" json:"published_date"`
ImageURL null.String `db:"image_url" json:"image_url,`
Description string `db:"description" json:"description"`
CreatedAt null.Time `db:"created_at" json:"created_at,`
UpdatedAt null.Time `db:"updated_at" json:"updated_at,`
DeletedAt null.Time `db:"deleted_at" json:"deleted_at,`
}
Have you read the api docs of sqlmock before asking?
@gmhafiz check Customize SQL query matching chapter of https://pkg.go.dev/github.com/DATA-DOG/go-sqlmock?utm_source=godoc
https://pkg.go.dev/github.com/DATA-DOG/go-sqlmock#readme-matching-arguments-like-time-time
Add this into your test file
type AnyTime struct{}
func (a AnyTime) Match(v driver.Value) bool {
_, ok := v.(time.Time)
return ok
}
and replace your data time.now() with AnyTime{}
mock.ExpectExec("UPDATE books set title").
WithArgs(mockBook.Title, mockBook.Description, mockBook.PublishedDate, mockBook.ImageURL.String, AnyTime{}, mockBook.BookID).
WillReturnResult(sqlmock.NewErrorResult(nil))