Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

外部環境への依存をテストする

2,814 views

Published on

Golangを使ってアプリを開発する際に、
外部環境への依存をテストするときのTIPS集です

Published in: Software
  • Be the first to comment

外部環境への依存をテストする

  1. 1. Golang.tokyo 2018/08/21 (Tue) ( @duck8823 )
  2. 2. • • @duck8823 • • / • • Kotlin • Golang
  3. 3. • • @duck8823 • • • • Kotlin • Golang
  4. 4. • • @duck8823 • • • • Kotlin • Golang
  5. 5. CI • duck8823/duci • Golang • Docker
  6. 6. CI • duck8823/duci • Golang • Docker
  7. 7. CI • duck8823/duci • Golang • Docker
  8. 8. Webhooks (HTTP) Commit Status (HTTP)
  9. 9. Webhooks (HTTP) Commit Status (HTTP)
  10. 10. Webhooks (HTTP) Commit Status (HTTP)
  11. 11. ( ) • Incoming HTTP Requests • Git Docker ( ) • • • Outgoing HTTP Requests
  12. 12. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  13. 13. Incoming HTTP Requests func HandlerFunc(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) body, _ := ioutil.ReadAll(r.Body) w.Write([]byte(fmt.Sprintf("Hello %s.", body))) } • HandlerFunc , . "
  14. 14. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } }
  15. 15. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } } http.ResponseWriter (*httptest.ResponseRecorder) *http.Request
  16. 16. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } }
  17. 17. HandlerFunc func Test_HandlerFunc(t *testing.T) { // given req := httptest.NewRequest("HELLO", "/", strings.NewReader("world")) rec := httptest.NewRecorder() // when HandlerFunc(rec, req) // then if rec.Code != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, rec.Code) } if !reflect.DeepEqual(rec.Body.Bytes(), []byte("Hello world.")) { t.Errorf("wont %+v, but got %+v","Hello world.", string(rec.Body.Bytes())) } } *httptest.ResponseRecorder
  18. 18. chi HandlerFunc • URL ("/{param}") HandlerFunc go-chi/chi // "/{param}" func ChiHandlerFunc(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) param := chi.URLParam(r.Context(), "param") w.Write([]byte(fmt.Sprintf("Hello %s.", param))) }
  19. 19. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec }
  20. 20. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec } context.Context
  21. 21. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec } *http.Request context.Context
  22. 22. chi HandlerFunc func TestChiHandlerFunc(t *testing.T) { // given ctx := chi.NewContext() ctx.Params.Add("param", "world") req := httptest.NewRequest("HELLO", "/", nil).WithContext(ctx) rec := httptest.NewRecorder() // when ChiHandlerFunc(rec, req) // then // ...check rec }
  23. 23. Incoming HTTP Requests • httptest • URL
  24. 24. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  25. 25. Git Docker (3rd ) • src-d/go-git moby/moby • •
  26. 26. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  27. 27. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  28. 28. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  29. 29. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  30. 30. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } • ( docker run )
  31. 31. type docker struct { moby *client.Client } func (d *docker) Run(image string, cmd ...string) { ctx := context.Background() _, err := d.moby.ImagePull(ctx, image, types.ImagePullOptions{}) con, _ := d.moby.ContainerCreate(ctx, &container.Config{ Image: image, Cmd: cmd }, nil, nil, "") err := d.moby.ContainerStart(ctx, con.ID, types.ContainerStartOptions{}) } . •
  32. 32. Mock • *client.Client type Moby interface { ImagePull(...) (...) ContainerCreate(...) (container.ContainerCreateCreatedBody, error) ContainerStart(...) error } type docker struct { moby Moby }
  33. 33. Mock • google/mock/gomock func TestDocker_Run(t *testing.T) { ctrl := gomock.NewController(t) mockMoby := NewMockMoby(ctrl) mockMoby.EXPECT(). ImagePull(gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes(). Return(nil, errors.New("error image pull")) docker := &docker{moby: mockMoby} docker.Run("centos", "echo", "hello world") }
  34. 34. Git Docker (3rd ) • • gomock • ( mock )
  35. 35. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  36. 36. package logger var Writer io.Writer = os.Stdout func Debug(message string) { fmt.Fprintf(Writer, "[DEBUG] %sn", message) } • • ( os.Stdout ) io.Writer (*os.File )
  37. 37. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  38. 38. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } } ) ( ).( 1 ( ( 2
  39. 39. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  40. 40. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  41. 41. func TestDebug(t *testing.T) { // given reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "[DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then writer.Close() actual, _ := ioutil.ReadAll(reader) if string(actual) != expected { t.Errorf("wont %s, but got %s", expected, actual) } }
  42. 42. • io.Writer io.Pipe
  43. 43. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  44. 44. • func Debug(message string) { now := time.Now().Format("2006-01-02 15:04:05") fmt.Fprintf(Writer, "%s [DEBUG] %sn", now, message) }
  45. 45. time.Now() • func Debug(message string) { now := clock.Now().Format("2006-01-02 15:04:05") fmt.Fprintf(Writer, "%s [DEBUG] %sn", now, message) } package clock import "time" var Now = func () time.Time { return time.Now() }
  46. 46. func TestDebug(t *testing.T) { // given jst, _ := time.LoadLocation("Asia/Tokyo") clock.Now = func() time.Time { return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst) } // and reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then // ... }
  47. 47. func TestDebug(t *testing.T) { // given jst, _ := time.LoadLocation("Asia/Tokyo") clock.Now = func() time.Time { return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst) } // and reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then // ... } .
  48. 48. func TestDebug(t *testing.T) { // given jst, _ := time.LoadLocation("Asia/Tokyo") clock.Now = func() time.Time { return time.Date(1987, time.March, 27, 09, 34, 00, 00, jst) } // and reader, writer, _ := os.Pipe() logger.Writer = writer // and expected := "1987-03-27 09:34:00 [DEBUG] Hello World.n" // when logger.Debug("Hello World.") // then // ... }
  49. 49. • ( ) • time.Now
  50. 50. • Incoming HTTP Requests • Git Docker ( ) ) • ( • • Outgoing HTTP Requests
  51. 51. Outgoing HTTP Requests • EO • IL P • H / D G RA
  52. 52. Outgoing HTTP Requests • 2 ) ) / ( ( func TestRequest(t *testing.T) { defer gock.Off() gock.New("http://example.com"). Post("/"). Reply(http.StatusOK) resp, _ := http.Post("http://example.com/", "application/json", nil) if resp.StatusCode != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode) } }
  53. 53. Outgoing HTTP Requests • 2 ) ) / ( ( func TestRequest(t *testing.T) { defer gock.Off() gock.New("http://example.com"). Post("/"). Reply(http.StatusOK) resp, _ := http.Post("http://example.com/", "application/json", nil) if resp.StatusCode != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode) } }
  54. 54. Outgoing HTTP Requests • 2 ) ) / ( ( func TestRequest(t *testing.T) { defer gock.Off() gock.New("http://example.com"). Post("/"). Reply(http.StatusOK) resp, _ := http.Post("http://example.com/", "application/json", nil) if resp.StatusCode != http.StatusOK { t.Errorf("wont %+v, but got %+v", http.StatusOK, resp.StatusCode) } }
  55. 55. Outgoing HTTP Requests • •
  56. 56. Webhooks (HTTP) Commit Status (HTTP)
  57. 57. Webhooks (HTTP) Commit Status (HTTP)
  58. 58. Webhooks (HTTP) Commit Status (HTTP)
  59. 59. Webhooks (HTTP) Commit Status (HTTP)
  60. 60. Webhooks (HTTP) Commit Status (HTTP) )& ) . & &(
  61. 61. Webhooks (HTTP) Commit Status (HTTP)
  62. 62. • oc • H • ) ( k • P T • p • he ) (M

×