0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Goでtime.Now()を使用している箇所のテストについて

Last updated at Posted at 2024-10-16

概要

time.Now()を使用している箇所のテストがしづらいと感じたのでその際のメモ

結論:contextにtime.Now()で生成する方法を採用

既存のコード

func Hoge() {
	todo := GetTodo()
	if IsTimeAfter(todo.StartTime) {
		...
	}
}

func IsTimeAfter(startTime time.Time) {
	currentDate := time.Now() // テストを実行する時刻に依存する。現在日時を固定にしたい。
	return startTime.After(currentDate)
}

対応方法

time.Now()で生成した現在時刻を引数にする

  • パッと修正可能
  • IsTimeAfter()のテストはしやすくなったが、Hoge()がtime.Nowに依存している状態
func Hoge() {
	todo := GetTodo()
	currentDate := time.Now()
	if IsTimeAfter(todo.StartTime, currentDate) {
		...
	}
}

func IsTimeAfter(startTime, currentDate time.Time) {
	return startTime.After(currentDate)
}

time.Now()で現在時刻を生成する処理をmock化

  • インターフェースを作成してテストの際はmockを使用する
  • 現在時刻を生成するためだけにインターフェースを定義したり、依存性を注入するのは少し過剰か?
type TimeProvider interface { // インターフェースを作成
    Now() time.Time
}

type RealTimeProvider struct{}

func (rtp RealTimeProvider) Now() time.Time { // 実装
    return time.Now()
}
type SomeService struct {
    TimeProvider timeprovider.TimeProvider
}

func NewSomeService(tp timeprovider.TimeProvider) *SomeService {
    return &SomeService{TimeProvider: tp}
}

func (s *SomeService) Hoge() {
    currentDate := s.TimeProvider.Now() // テストの際はモックを使用する
		if IsTimeAfter(todo.StartTime, currentDate) {
				...
		}
}

func IsTimeAfter(startTime, currentDate time.Time) {
	return startTime.After(currentDate)
}

middlewareでtime.Now()で現在時刻を生成してcontextに入れておく

  • mock化より実装はシンプルになる
// middleware(echoを使用)
func NewCurrentTime() echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			n := time.Now()
			ctx := context.WithValue(c.Request().Context(), "current_time", n)
			c.SetRequest(c.Request().WithContext(ctx))
			return next(c)
		}
	}
}
// route.go(echoを使用)
func SetupRouter() *echo.Echo {
	e := echo.New()
	e.Use(middleware.NewCurrentTime()) // ここで使用
	e.GET("/hoge", hogeHandler.Hoge)
	.
	.
func (s *SomeService) Hoge(ctx context.Context) {
    currentDate := ctx.Value("current_time").(time.Time) // ここでcontextから取得
		if IsTimeAfter(todo.StartTime, currentDate) {
				...
		}
}

// 現在時刻を引数にしているが、関数内でcontextから時刻を取得してもよい。迷う。
func IsTimeAfter(startTime, currentDate time.Time) {
	return startTime.After(currentDate)
}

参考にした記事

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?