Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CustomMiddlewareのテストをする方法

 CustomMiddlewareのテストをする方法

GoのWebFrameworkのechoを使ったカスタムミドルウェアの作り方とテスト方法についての発表です。

サンプルコード
https://github.com/saicologic/echo_custom_middleware_sample

Satoru Mikami

December 14, 2022
Tweet

More Decks by Satoru Mikami

Other Decks in Technology

Transcript

  1. 自己紹介 Satoru Mikami Backend Engineer at Voicy Twitter: @saicologic Go

    と私 Go で開発して2 年になりました。 Go Conference 2022 Spring に登壇しました 来年に向けてプロポーザルを考え中 スライドは、slidev で作りました。
  2. ミドルウェアとテスト レスポンス結果 verbose 今回は、リクエストヘッダーで受け取ったUserID をContext に格納する例を紹介します 1 $ curl -v

    http://localhost:1323 -H "X-USER-ID:12345678" 1 12345678 1 * Trying 127.0.0.1:1323... 2 * Connected to localhost (127.0.0.1) port 1323 (#0) 3 > GET / HTTP/1.1 4 > Host: localhost:1323 5 > User-Agent: curl/7.79.1 6 > Accept: */* 7 > X-USER-ID:12345678
  3. main.go 1 // Handler 2 func GetUser(c echo.Context) error {

    3 ctx := GetContextValues(c) 4 return c.String(http.StatusOK, ctx.UserID) 5 } 6 7 func initEcho() *echo.Echo { 8 e := echo.New() 9 e.Use(setUserIDMiddleware) 10 e.GET("/", GetUser) 11 return e 12 } 13 14 func main() { 15 e := initEcho() 16 e.Logger.Fatal(e.Start(":1323")) 17 }
  4. main 15 e := initEcho() 1 // Handler 2 func

    GetUser(c echo.Context) error { 3 ctx := GetContextValues(c) 4 return c.String(http.StatusOK, ctx.UserID) 5 } 6 7 func initEcho() *echo.Echo { 8 e := echo.New() 9 e.Use(setUserIDMiddleware) 10 e.GET("/", GetUser) 11 return e 12 } 13 14 func main() { 16 e.Logger.Fatal(e.Start(":1323")) 17 }
  5. initEcho テストで使う共通で使う 7 func initEcho() *echo.Echo { 8 e :=

    echo.New() 9 e.Use(setUserIDMiddleware) 10 e.GET("/", GetUser) 11 return e 12 } 1 // Handler 2 func GetUser(c echo.Context) error { 3 ctx := GetContextValues(c) 4 return c.String(http.StatusOK, ctx.UserID) 5 } 6 13 14 func main() { 15 e := initEcho() 16 e.Logger.Fatal(e.Start(":1323")) 17 }
  6. setUserIDMiddleware テストで使う共通で使う 9 e.Use(setUserIDMiddleware) 1 // Handler 2 func GetUser(c

    echo.Context) error { 3 ctx := GetContextValues(c) 4 return c.String(http.StatusOK, ctx.UserID) 5 } 6 7 func initEcho() *echo.Echo { 8 e := echo.New() 10 e.GET("/", GetUser) 11 return e 12 } 13 14 func main() { 15 e := initEcho() 16 e.Logger.Fatal(e.Start(":1323")) 17 }
  7. setUserIDMiddleware 1 func setUserIDMiddleware(next echo.HandlerFunc) echo.HandlerFunc { 2 return func(c

    echo.Context) error { 3 userID := c.Request().Header.Get("X-USER-ID") 4 ctx := GetContextValues(c) 5 ctx.UserID = userID 6 return next(c) 7 } 8 }
  8. setUserIDMiddleware 4 ctx := GetContextValues(c) 1 func setUserIDMiddleware(next echo.HandlerFunc) echo.HandlerFunc

    { 2 return func(c echo.Context) error { 3 userID := c.Request().Header.Get("X-USER-ID") 5 ctx.UserID = userID 6 return next(c) 7 } 8 }
  9. GetContextValues 9 func GetContextValues(c echo.Context) *ContextValues { 10 values, ok

    := c.Get(string(ContextValuesKey)).(*ContextValues) 11 if values == nil || !ok { 12 values = &ContextValues{} 13 c.Set(string(ContextValuesKey), values) 14 } 15 return values 16 } 1 type ContextValues struct { 2 UserID string 3 } 4 5 type contextValuesKey string 6 7 const ContextValuesKey = contextValuesKey("ContextValuesKey") 8
  10. github.com/labstack/echo/v4/context.go 1 type ( 2 // Context represents the context

    of the current HTTP request. It holds request and 3 // response objects, path, path parameters, data and registered handler. 4 Context interface { 5 省略 6 // Get retrieves data from the context. 7 Get(key string) interface{} 8 9 // Set saves data in the context. 10 Set(key string, val interface{}) 11 } 12 )
  11. GetContextValues 1 type ContextValues struct { 2 UserID string 3

    } 4 5 type contextValuesKey string 6 7 const ContextValuesKey = contextValuesKey("ContextValuesKey") 8 9 func GetContextValues(c echo.Context) *ContextValues { 10 values, ok := c.Get(string(ContextValuesKey)).(*ContextValues) 11 if values == nil || !ok { 12 values = &ContextValues{} 13 c.Set(string(ContextValuesKey), values) 14 } 15 return values 16 }
  12. GetUser 2 func GetUser(c echo.Context) error { 3 ctx :=

    GetContextValues(c) 4 return c.String(http.StatusOK, ctx.UserID) 5 } 1 // Handler 6 7 func initEcho() *echo.Echo { 8 e := echo.New() 9 e.Use(setUserIDMiddleware) 10 e.GET("/", GetUser) 11 return e 12 } 13 14 func main() { 15 e := initEcho() 16 e.Logger.Fatal(e.Start(":1323")) 17 }
  13. main_test.go 1 func TestCustomHandler(t *testing.T) { 2 t.Run("Add userID", func(t

    *testing.T) { 3 userID := "12345678" 4 method := echo.GET 5 target := "/" 6 body := strings.NewReader("") 7 req := httptest.NewRequest(method, target, body) 8 req.Header.Set("X-USER-ID", userID) 9 rec := httptest.NewRecorder() 10 11 e := initEcho() 12 e.ServeHTTP(rec, req) 13 14 ctx := e.AcquireContext() 15 defer e.ReleaseContext(ctx) 16 c := GetContextValues(ctx) 17 res := rec.Result() 18 assert.Equal(t, 200, res.StatusCode) 19 assert.Equal(t, userID, rec.Body.String()) 20 assert.Equal(t, userID, c.UserID) 21 }) 22 }
  14. Package import 1 import ( 2 "net/http/httptest" 3 "strings" 4

    "testing" 5 6 echo "github.com/labstack/echo/v4" 7 "github.com/stretchr/testify/assert" 8 )
  15. TestCustomHandler 3 userID := "12345678" 1 func TestCustomHandler(t *testing.T) {

    2 t.Run("Add userID", func(t *testing.T) { 4 method := echo.GET 5 target := "/" 6 body := strings.NewReader("") 7 req := httptest.NewRequest(method, target, body) 8 req.Header.Set("X-USER-ID", userID) 9 rec := httptest.NewRecorder() 10 11 e := initEcho() 12 e.ServeHTTP(rec, req) 13 14 ctx := e.AcquireContext() 15 defer e.ReleaseContext(ctx) 16 c := GetContextValues(ctx) 17 res := rec.Result() 18 assert.Equal(t, 200, res.StatusCode) 19 assert.Equal(t, userID, rec.Body.String()) 20 assert.Equal(t, userID, c.UserID) 21 }) 22 }
  16. TestCustomHandler 4 method := echo.GET 5 target := "/" 6

    body := strings.NewReader("") 7 req := httptest.NewRequest(method, target, body) 8 req.Header.Set("X-USER-ID", userID) 9 rec := httptest.NewRecorder() 1 func TestCustomHandler(t *testing.T) { 2 t.Run("Add userID", func(t *testing.T) { 3 userID := "12345678" 10 11 e := initEcho() 12 e.ServeHTTP(rec, req) 13 14 ctx := e.AcquireContext() 15 defer e.ReleaseContext(ctx) 16 c := GetContextValues(ctx) 17 res := rec.Result() 18 assert.Equal(t, 200, res.StatusCode) 19 assert.Equal(t, userID, rec.Body.String()) 20 assert.Equal(t, userID, c.UserID) 21 }) 22 }
  17. TestCustomHandler 11 e := initEcho() 12 e.ServeHTTP(rec, req) 1 func

    TestCustomHandler(t *testing.T) { 2 t.Run("Add userID", func(t *testing.T) { 3 userID := "12345678" 4 method := echo.GET 5 target := "/" 6 body := strings.NewReader("") 7 req := httptest.NewRequest(method, target, body) 8 req.Header.Set("X-USER-ID", userID) 9 rec := httptest.NewRecorder() 10 13 14 ctx := e.AcquireContext() 15 defer e.ReleaseContext(ctx) 16 c := GetContextValues(ctx) 17 res := rec.Result() 18 assert.Equal(t, 200, res.StatusCode) 19 assert.Equal(t, userID, rec.Body.String()) 20 assert.Equal(t, userID, c.UserID) 21 }) 22 }
  18. initEcho 1 func initEcho() *echo.Echo { 2 e := echo.New()

    3 e.Use(setUserIDMiddleware) 4 e.GET("/", GetUser) 5 return e 6 }
  19. TestCustomHandler 14 ctx := e.AcquireContext() 15 defer e.ReleaseContext(ctx) 16 c

    := GetContextValues(ctx) 17 res := rec.Result() 18 assert.Equal(t, 200, res.StatusCode) 19 assert.Equal(t, userID, rec.Body.String()) 20 assert.Equal(t, userID, c.UserID) 1 func TestCustomHandler(t *testing.T) { 2 t.Run("Add userID", func(t *testing.T) { 3 userID := "12345678" 4 method := echo.GET 5 target := "/" 6 body := strings.NewReader("") 7 req := httptest.NewRequest(method, target, body) 8 req.Header.Set("X-USER-ID", userID) 9 rec := httptest.NewRecorder() 10 11 e := initEcho() 12 e.ServeHTTP(rec, req) 13 21 }) 22 }
  20. TestCustomHandler 14 ctx := e.AcquireContext() 15 defer e.ReleaseContext(ctx) 16 c

    := GetContextValues(ctx) 17 res := rec.Result() 1 func TestCustomHandler(t *testing.T) { 2 t.Run("Add userID", func(t *testing.T) { 3 userID := "12345678" 4 method := echo.GET 5 target := "/" 6 body := strings.NewReader("") 7 req := httptest.NewRequest(method, target, body) 8 req.Header.Set("X-USER-ID", userID) 9 rec := httptest.NewRecorder() 10 11 e := initEcho() 12 e.ServeHTTP(rec, req) 13 18 assert.Equal(t, 200, res.StatusCode) 19 assert.Equal(t, userID, rec.Body.String()) 20 assert.Equal(t, userID, c.UserID) 21 }) 22 }
  21. echo AcquireContext/ReleaseContext github.com/labstack/echo/v4/echo.go 1 // AcquireContext returns an empty `Context`

    instance from the pool. 2 // You must return the context by calling `ReleaseContext()`. 3 func (e *Echo) AcquireContext() Context { 4 return e.pool.Get().(Context) 5 } 6 7 // ReleaseContext returns the `Context` instance back to the pool. 8 // You must call it after `AcquireContext()`. 9 func (e *Echo) ReleaseContext(c Context) { 10 e.pool.Put(c) 11 }
  22. TestCustomHandler 17 res := rec.Result() 18 assert.Equal(t, 200, res.StatusCode) 19

    assert.Equal(t, userID, rec.Body.String()) 20 assert.Equal(t, userID, c.UserID) 1 func TestCustomHandler(t *testing.T) { 2 t.Run("Add userID", func(t *testing.T) { 3 userID := "12345678" 4 method := echo.GET 5 target := "/" 6 body := strings.NewReader("") 7 req := httptest.NewRequest(method, target, body) 8 req.Header.Set("X-USER-ID", userID) 9 rec := httptest.NewRecorder() 10 11 e := initEcho() 12 e.ServeHTTP(rec, req) 13 14 ctx := e.AcquireContext() 15 defer e.ReleaseContext(ctx) 16 c := GetContextValues(ctx) 21 }) 22 }