Skip to content

単体テスト作成

echoを使ったハンドラのテストを作成します。

作成手順

cf. https://echo.labstack.com/docs/testing

handlerテスト作成

  • users_handler_test.go
package handler

import (
    "bytes"
    "easyapp/internal/usecase/params"
    "errors"
    "net/http"
    "net/http/httptest"
    "net/url"
    "testing"

    "github.com/labstack/echo/v4"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

type mockUsersUsecase struct {
    mock.Mock
}

func (m *mockUsersUsecase) Login(in params.LoginIn) params.LoginOut {
    args := m.Called(in)
    return args.Get(0).(params.LoginOut)
}

func (m *mockUsersUsecase) Me(in params.MeIn) (params.MeOut, error) {
    args := m.Called(in)
    return args.Get(0).(params.MeOut), args.Error(1)
}

func Test_UsersHandler_Login(t *testing.T) {

    tests := []struct {
        name               string                       // テストケース名
        requestBody        string                       // リクエストボディ
        setupMock          func(mock *mockUsersUsecase) // モック設定
        expectedStatusCode int                          // 期待されるHTTPステータス
        expectedBody       string                       // 期待されるレスポンスボディ
    }{
        {
            name:        "success",
            requestBody: `{"name": "nob", "password": "passwd"}`,
            setupMock: func(mock *mockUsersUsecase) {
                mock.On(
                    "Login",
                    params.NewLoginIn(
                        "nob",
                        "passwd",
                    ),
                ).Return(
                    params.NewLoginOut(true),
                )
            },
            expectedStatusCode: http.StatusOK,
            expectedBody:       `{"valid": true}`,
        },
        {
            name:               "invalid request",
            requestBody:        `{"name": "nob", "password": "passwd"`, // 末尾の"}"なし
            setupMock:          func(mock *mockUsersUsecase) {},
            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(mockUsersUsecase)
        testcase.setupMock(mockUsecase)
        h := NewUsersHandler(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_UsersHandler_Me(t *testing.T) {

    tests := []struct {
        name               string                       // テストケース名
        requestParam       map[string]string            // リクエストパラメータ
        setupMock          func(mock *mockUsersUsecase) // モック設定
        expectedStatusCode int                          // 期待されるHTTPステータス
        expectedBody       string                       // 期待されるレスポンスボディ
    }{
        {
            name:         "success",
            requestParam: map[string]string{"name": "nob"},
            setupMock: func(mock *mockUsersUsecase) {
                mock.On("Me", 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(mock *mockUsersUsecase) {
                mock.On("Me", 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(mockUsersUsecase)
        testcase.setupMock(mockUsecase)
        h := NewUsersHandler(mockUsecase)

        if assert.NoError(t, h.Me(c)) {
            assert.Equal(t, testcase.expectedStatusCode, rec.Code)
            assert.JSONEq(t, testcase.expectedBody, rec.Body.String())
        }
    }
}