1
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.Time型をGomockで扱ってたらハマった話

Last updated at Posted at 2025-04-08

初めての技術記事書いてみます。
最近Goを業務で書き始めた初心者なので、あたたかく見守ってください。

状況

GoMockを使ったテストコードを書いてると、以下のようなGomockのエラーに遭遇しました。

sample.go:17: Unexpected call to *mock_sample.MockSample.Process([2025-04-08 00:00:00 +0900 JST]) at sample/sample.go:17 because:
    expected call at C:/Users/fuuta/Documents/home/program/study-go/app/sample/sample_test.go:34 doesn't match the argument at index 0.
    Got: 2025-04-08 00:00:00 +0900 JST (time.Time)
    Want: is equal to 2025-04-08 00:00:00 +0900 JST (time.Time)

抜粋してみると完全に一致していることがわかります。

Got: 2025-04-08 00:00:00 +0900 JST (time.Time)
Want: is equal to 2025-04-08 00:00:00 +0900 JST (time.Time)

原因

「何が違うねん!?」と最初思いましたが、どうやらTime型のフィールドの一つであるLocationが異なるようでした。
今回の例においては LocalAsia/Tokyo とで二つのLocation構造体があり、Got側とwant側で日時は同じだが異なるLocationを持ったTime構造体の比較を行っていたのが原因でした。

テスト落ちの出力で JST 表記になっている原因は、LocationにLocalが指定されていると日本にいる場合は JST と出る仕組みなんだろうと推測してます。

最後に簡単な検証用ソースを添付しておきます。

sample_test.go
package sample

import (
	"fmt"
	"test/mock_sample"
	"testing"
	"time"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
)

// gomock使った検証用
func Test1(t *testing.T) {
	location, _ := time.LoadLocation("Local")

	layout := "2006-01-02 15:04:05"
	value := "2025-04-08 00:00:00"

	time1, _ := time.ParseInLocation(layout, value, location)

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	m := mock_sample.NewMockSample(ctrl)

	m.EXPECT().Process(time1)

	Process(m)
}

// gomock使わない検証用
func Test2(t *testing.T) {
	locationLocal, _ := time.LoadLocation("Local")
	locationJST, _ := time.LoadLocation("Asia/Tokyo")

	layout := "2006-01-02 15:04:05"
	value := "2025-04-08 00:00:00"

	time1, _ := time.ParseInLocation(layout, value, locationLocal)
	time2, _ := time.ParseInLocation(layout, value, locationJST)

	t.Logf("%v\n", time1)
	t.Logf("%v\n", time2)
	t.Logf("%v\n", time1 == time2)
	t.Logf("%v\n", time1.Equal(time2))
	t.Logf("%v\n", fmt.Sprintf("%v (%T)", time1, time1))
	t.Logf("%v\n", fmt.Sprintf("%v (%T)", time2, time2))

	assert.Equal(t, time1, time2)
    // expected: time.Date(2025, time.April, 8, 0, 0, 0, 0, time.Local)
    // actual  : time.Date(2025, time.April, 8, 0, 0, 0, 0, time.Location("Asia/Tokyo"))
}

sample.go
package sample

import "time"

type Sample interface {
	Process(time time.Time)
}

func Process(sample Sample) {
	location, _ := time.LoadLocation("Asia/Tokyo")

	layout := "2006-01-02 15:04:05"
	value := "2025-04-08 00:00:00"

	time, _ := time.ParseInLocation(layout, value, location)

	sample.Process(time)
}

Test2 の方を見ると分かりますが、 testify/assertassert.Equal を使うとTime型も構造体としてフィールド単位で出力してくれるので違いが明確にわかります。
Gomock は"%v"で出力する実装となっており、この場合Time型固有のフォーマットで出力されます。普段は見やすいものの今回はそれが裏目に出た形ですね。

まとめ

どの言語も日付周りは厄介ですね。。。

参考

1
0
0

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
1
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?