単体テスト作成
echoを使ったハンドラのテストを作成します。
作成手順
cf. https://echo.labstack.com/docs/testing
handlerテスト作成
user_handler_test.go
package handler
import (
"bytes"
"context"
"easyapp/internal/usecase/params"
"errors"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type mockUserUsecase struct {
mock.Mock
}
func (m *mockUserUsecase) Login(ctx context.Context, in params.LoginIn) params.LoginOut {
args := m.Called(ctx, in)
return args.Get(0).(params.LoginOut)
}
func (m *mockUserUsecase) Me(ctx context.Context, in params.MeIn) (params.MeOut, error) {
args := m.Called(in)
return args.Get(0).(params.MeOut), args.Error(1)
}
func Test_UserHandler_Login(t *testing.T) {
tests := []struct {
name string // テストケース名
requestBody string // リクエストボディ
setupMock func(m *mockUserUsecase) // モック設定
expectedStatusCode int // 期待されるHTTPステータス
expectedBody string // 期待されるレスポンスボディ
}{
{
name: "success",
requestBody: `{"name": "nob", "password": "passwd"}`,
setupMock: func(m *mockUserUsecase) {
m.On(
"Login",
mock.Anything,
params.NewLoginIn(
"nob",
"passwd",
),
).Return(
params.NewLoginOut(true),
)
},
expectedStatusCode: http.StatusOK,
expectedBody: `{"valid": true}`,
},
{
name: "invalid request",
requestBody: `{"name": "nob", "password": "passwd"`, // 末尾の"}"なし
setupMock: func(m *mockUserUsecase) {},
expectedStatusCode: http.StatusBadRequest,
expectedBody: `{"message": "Bad request"}`,
},
}
for _, testcase := range tests {
req := httptest.NewRequest(
http.MethodPost,
"/login",
bytes.NewBuffer([]byte(testcase.requestBody)),
)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := echo.New().NewContext(req, rec)
mockUsecase := new(mockUserUsecase)
testcase.setupMock(mockUsecase)
h := NewUserHandler(mockUsecase)
if assert.NoError(t, h.Login(c)) {
assert.Equal(t, testcase.expectedStatusCode, rec.Code)
assert.JSONEq(t, testcase.expectedBody, rec.Body.String())
}
}
}
func Test_UserHandler_Me(t *testing.T) {
tests := []struct {
name string // テストケース名
requestParam map[string]string // リクエストパラメータ
setupMock func(m *mockUserUsecase) // モック設定
expectedStatusCode int // 期待されるHTTPステータス
expectedBody string // 期待されるレスポンスボディ
}{
{
name: "success",
requestParam: map[string]string{"name": "nob"},
setupMock: func(m *mockUserUsecase) {
m.On(
"Me",
mock.Anything,
params.NewMeIn("nob"),
).Return(
params.NewMeOut(
"nob",
13,
),
nil,
)
},
expectedStatusCode: http.StatusOK,
expectedBody: `{"name": "nob", "age": 13}`,
},
{
name: "usecase error",
requestParam: map[string]string{"name": "nob"},
setupMock: func(m *mockUserUsecase) {
m.On(
"Me",
mock.Anything,
params.NewMeIn("nob"),
).Return(
*new(params.MeOut),
errors.New("不正なユーザ名です。"),
)
},
expectedStatusCode: http.StatusInternalServerError,
expectedBody: `{"message": "Internal server error"}`,
},
}
for _, testcase := range tests {
q := make(url.Values)
q.Set("name", testcase.requestParam["name"])
req := httptest.NewRequest(http.MethodGet, "/me?"+q.Encode(), nil)
rec := httptest.NewRecorder()
c := echo.New().NewContext(req, rec)
mockUsecase := new(mockUserUsecase)
testcase.setupMock(mockUsecase)
h := NewUserHandler(mockUsecase)
if assert.NoError(t, h.Me(c)) {
assert.Equal(t, testcase.expectedStatusCode, rec.Code)
assert.JSONEq(t, testcase.expectedBody, rec.Body.String())
}
}
}