Slide 1

Slide 1 text

Goͷ࣌ࠁʹؔ͢Δςετ 2017/07/07 Umeda.go #2

Slide 2

Slide 2 text

ࣗݾ঺հ • Տ໺ ݈ଠ࿕ (@kawaken) • γφδʔϚʔέςΟϯάʢגʣ • ϓογϡ௨஌ج൫ɺLINE഑৴ج൫ • Go͸3೥͘Β͍ • goaͷίʔυεχϖοτ࡞ͬͯ·͢

Slide 3

Slide 3 text

࣌ࠁͷςετͷ೉͠͞ʁ

Slide 4

Slide 4 text

func CanDeliver() bool { hour := time.Now().Hour() // 8࣌ʙ20࣌ͳΒ഑৴Մೳ return 8 <= hour && hour <= 20 } ࣌ࠁΛऔಘͯ͠൑அ͢Δfunc

Slide 5

Slide 5 text

ςετํ਑ • 8࣌ʙ20࣌: trueͰ͋Δ͜ͱΛ֬ೝ͢Δ • 21࣌ʙ7࣌: falseͰ͋Δ͜ͱΛ֬ೝ͢Δ

Slide 6

Slide 6 text

μϝͳςετ func TestCanDeliver(t *testing.T) { hour := time.Now().Hour() expected := 8 <= hour && hour <= 20 // ಉ͡ॲཧ result := CanDeliver() if expected == result { t.Log("OK") } else { t.Fatal("NG") } }

Slide 7

Slide 7 text

࣌ࠁͷςετͷ೉͠͞ • time.Now ͸ৗʹҧ͏஋Λฦ͢ • ͕࣌ؒܦա͠ͳ͍ͱύλʔϯΛ໢ཏͰ͖ͳ͍

Slide 8

Slide 8 text

͍ΖΜͳςετͷύλʔϯ • ݄ลΓͷि਺ • څྉ೔͕ٳ೔ • य़෼ͷ೔ɺळ෼ͷ೔ • ৼସٳ೔ • 2݄29೔

Slide 9

Slide 9 text

࣮૷ͱςετͷύλʔϯ

Slide 10

Slide 10 text

࣮૷ͱςετͷύλʔϯ 1. ֎෦͔Βͷ஫ೖ 2. ελϒ 3. αʔυύʔςΟʔͷύοέʔδ

Slide 11

Slide 11 text

1. ֎෦͔Βͷ஫ೖ

Slide 12

Slide 12 text

func CanDeliver(hour int) bool { // 8࣌ʙ20࣌ͳΒ഑৴Մೳ return 8 <= hour && hour <= 20 } ͦ΋ͦ΋time.NowΛ࢖͏ඞཁ͕ͳ͍

Slide 13

Slide 13 text

func TestCanDeliver(t *testing.T) { cases := []struct { hour int want bool }{ {7, false}, {8, true}, {20, true}, {21, false}, } for _, c := range cases { got := CanDeliver(c.hour) if got != c.want { t.Errorf("CanDeliver(%d) => %t, want %t", c.hour, got, c.want) } } }

Slide 14

Slide 14 text

2. ελϒ

Slide 15

Slide 15 text

var now = time.Now // time.NowΛnowʹೖΕ͓ͯ͘ func CanDeliver() bool { hour := now().Hour() // nowΛ࢖༻ͯ࣌͠ࠁΛऔಘ // 8࣌ʙ20࣌ͳΒ഑৴Մೳ return 8 <= hour && hour <= 20 } now Λςετ࣌ʹஔ͖׵͑Δ

Slide 16

Slide 16 text

func fakeHour(hour int) { // ݻఆͷtime.TimeΛฦ͢funcͰnowΛஔ͖׵͑Δ now = func() time.Time { return time.Date(2017, 7, 7, hour, 0, 0, 0, time.Local) } } func TestCanDeliver(t *testing.T) { // snip... for _, c := range cases { fakeHour(c.hour) // fakeHourͰ࣌ࠁΛॻ͖׵͑Δ got := CanDeliver() if got != c.want { t.Errorf("hour: %d, CanDeliver() => %t, want %t", c.hour, got, c.want) } } now = time.Now // reset time }

Slide 17

Slide 17 text

3. αʔυύʔςΟʔͷύοέʔδ

Slide 18

Slide 18 text

code.cloudfoundry.org/clock • timeͷ୅ସͱͯ͠࢖͏ύοέʔδ • ؔ਺͕๛෋ • ςετ࣌ʹ͸ɺclock/fakeclock/FakeClockΛ࢖༻

Slide 19

Slide 19 text

var myClock = clock.NewClock() // myClockʹclock.ClockΛ୅ೖ func CanDeliver() bool { hour := myClock.Now().Hour() // myClockΛ࢖༻ͯ࣌͠ࠁΛऔಘ // 8࣌ʙ20࣌ͳΒ഑৴Մೳ return 8 <= hour && hour <= 20 } myClock Λςετ࣌ʹஔ͖׵͑Δ

Slide 20

Slide 20 text

func fakeHour(hour int) { // ࢦఆ͞Εͨ࣌ࠁ͔Β։࢝͢ΔΑ͏ʹFakeClockͰஔ͖׵͑Δ myClock = fakeclock.NewFakeClock(time.Date(2017, 7, 7, hour, 0, 0, 0, time.Local)) } func TestCanDeliver(t *testing.T) { // snip... for _, c := range cases { fakeHour(c.hour) // fakeHourͰ࣌ࠁΛॻ͖׵͑Δ got := CanDeliver() if got != c.want { t.Errorf("hour: %d, CanDeliver() => %t, want %t", c.hour, got, c.want) } } myClock = clock.NewClock() // reset time }

Slide 21

Slide 21 text

ϞϠϞϠϙΠϯτ • ґଘੑΛ֎෦͔Β஫ೖ • Ͳ͔͜Ͱ time.Now ͸ݺ͹ͳ͍ͱ͍͚ͳ͍ • ελϒ΍αʔυύʔςΟʔύοέʔδ • time.Now Λ௚઀࢖༻ͯ͠͸͍͚ͳ͍

Slide 22

Slide 22 text

ૉ௚ʹtime.Now࢖͍͍ͨ

Slide 23

Slide 23 text

4. ֎෦͔Βͷ஫ೖ̎

Slide 24

Slide 24 text

func CanDeliver(hour int) bool { // 8࣌ʙ20࣌ͳΒ഑৴Մೳ return 8 <= hour && hour <= 20 } func CanDeliverNow() bool { hour := time.Now().Hour() return CanDeliver(hour) }

Slide 25

Slide 25 text

5. Monkey patch

Slide 26

Slide 26 text

github.com/bouk/monkey • GoͰϞϯΩʔύονΛ࣮ݱ͢Δύοέʔδ • ؔ਺ϙΠϯλΛࠩ͠ସ͑ͨΓͰ͖Δ • શͯͷ؀ڥͰಈ࡞͢ΔΘ͚Ͱ͸ͳ͍Έ͍ͨ

Slide 27

Slide 27 text

func CanDeliver() bool { hour := time.Now().Hour() // 8࣌ʙ20࣌ͳΒ഑৴Մೳ return 8 <= hour && hour <= 20 }

Slide 28

Slide 28 text

func fakeHour(hour int) { // time.NowΛࢦఆͷ࣌ࠁΛฦ͢funcͰஔ͖׵͑Δ monkey.Patch( time.Now, func() time.Time { return time.Date(2017, 7, 7, hour, 0, 0, 0, time.Local) }, ) } func TestCanDeliver(t *testing.T) { // snip... for _, c := range cases { fakeHour(c.hour) // fakeHourͰ࣌ࠁΛॻ͖׵͑Δ got := CanDeliver() if got != c.want { t.Errorf("hour: %d, CanDeliver() => %t, want %t", c.hour, got, c.want) } } monkey.Unpatch(time.Now) // reset time }

Slide 29

Slide 29 text

6. timeύοέʔδΛ֦ு͢Δ

Slide 30

Slide 30 text

// time.Now func Now() Time { sec, nsec := now() return Time{sec + unixToInternal, nsec, Local} }

Slide 31

Slide 31 text

NowΛ͍͡Ε͹͍͍ͷͰ͸ʁ

Slide 32

Slide 32 text

/* src/time/time.go */ var fakeTime Time // χηͷ࣌ࠁ func Fake(t Time) { fakeTime = t } func ResetFake() { fakeTime = Time{} } func Now() Time { if !fakeTime.IsZero() { return fakeTime } sec, nsec := now() return Time{sec + unixToInternal, nsec, Local} }

Slide 33

Slide 33 text

func CanDeliver() bool { hour := time.Now().Hour() // 8࣌ʙ20࣌ͳΒ഑৴Մೳ return 8 <= hour && hour <= 20 }

Slide 34

Slide 34 text

func fakeHour(hour int) { time.Fake(time.Date(2017, 7, 7, hour, 0, 0, 0, time.Local)) } func TestCanDeliver(t *testing.T) { // snip... for _, c := range cases { fakeHour(c.hour) // fakeHourͰ࣌ࠁΛॻ͖׵͑Δ got := CanDeliver() if got != c.want { t.Errorf("hour: %d, CanDeliver() => %t, want %t", c.hour, got, c.want) } } time.ResetFake() // reset time }

Slide 35

Slide 35 text

΄ΜͱʹͣΕͯΜͷ͔ͳʁʁʁ % /usr/local/go1.8.3_faketime/bin/go test -v ./timepkg === RUN TestCanDeliver --- PASS: TestCanDeliver (0.00s) sample_test.go:22: 2017-07-07 07:00:00 +0900 JST sample_test.go:22: 2017-07-07 08:00:00 +0900 JST sample_test.go:22: 2017-07-07 20:00:00 +0900 JST sample_test.go:22: 2017-07-07 21:00:00 +0900 JST PASS ok github.com/kawaken/golang-time-testing/timepkg 0.466s

Slide 36

Slide 36 text

·ͱΊ ࣌ࠁͷςετ͸೉͍͕͠ɺճආํ๏͸͋Δ 1. ࣌ࠁͷऔಘͱɺ࣌ࠁΛॲཧ͢Δͱ͜ΖΛ෼͚Δ 2. code.cloudfoundry.org/clock ͕ແ೉ʁ 3. monkey΋͋Γʁ