😎

DBアクセスをモック化した自動テスト(Go+echo)

2023/04/22に公開

バージョン

go version go1.20.1 darwin/amd64
echo v3.3.10-dev

以下の記事を参考にさせていただきました
https://dev.classmethod.jp/articles/test-golang-echo-and-mysql/

ユーザーを一意に特定できる情報でDBを参照して、ユーザーが特定できた場合のテストと、ユーザーが特定できなかった場合のエラーとするテストコードです。

route.go
func Requestroute() {
	router := NewRouter()
	router.Logger.Fatal(router.Start(":" + config.Port))
}

func NewRouter() *echo.Echo {
	e := echo.New()
	e.GET("/get", func(ctx echo.Context) error {
		handler := IniHandler(models.NewUserModel())
		return handler.GetReq(ctx)
	})
	return e
}
usersHandler.go
package controllers

import (
	"fmt"
	"itodo/app/models"
	"net/http"

	"github.com/labstack/echo"
)

type (
	userhandler struct {
		user models.UserModel
	}
)

func IniHandler(u models.UserModel) *userhandler {
	return &userhandler{u}
}

func (h userhandler) GetReq(c echo.Context) error {
	var userid int = 1
	h.user.Fetch(userid)
	var uuid string = h.user.GetName()
	if uuid == "" {
		return echo.NewHTTPError(http.StatusNotFound)
	}
	return c.String(http.StatusOK, uuid)
}

モデル

users.go
type (
	UserModel interface {
		Fetch(id int)
		GetName() string
	}
	userModel struct {
		uuid string
	}
)

func NewUserModel() *userModel {
	return &userModel{}
}

func (u *userModel) GetName() string {
	return u.uuid
}

func (u *userModel) Fetch(id int) {
	if id < 1 {
		return
	}
	err := Db.QueryRow("SELECT uuid FROM users WHERE id=? LIMIT 1", id).Scan(&u.uuid)
	switch {
	case err == sql.ErrNoRows:
		u.uuid = ""
	case err != nil:
		panic(err.Error())
	}
}

テスト

usersHandler_test.go
package controllers

import (
	"net/http"
	"net/http/httptest"
	"testing"

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

type userModelStub struct {
	uuid string
}

func (u *userModelStub) Fetch(id int)    {}
func (u *userModelStub) GetName() string { return u.uuid }

func Test_Getreq_Ok(t *testing.T) {
	e := echo.New()
	req := httptest.NewRequest(http.MethodGet, "/req", nil)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	u := &userModelStub{uuid: "test"}
	h := IniHandler(u)
	h.GetReq(c)
	if assert.NoError(t, h.GetReq(c)) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, "test", rec.Body.String())
	}
}

func Test_Getreq_404(t *testing.T) {
	e := echo.New()
	req := httptest.NewRequest(http.MethodGet, "/req", nil)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	u := &userModelStub{uuid: ""}
	h := IniHandler(u)
	err := h.GetReq(c)

	if assert.NotNil(t, err) {
		err, res := err.(*echo.HTTPError)
		if res {
			assert.Equal(t, http.StatusNotFound, err.Code)
			assert.Equal(t, "Not Found", err.Message)
		}
	}
}

Go(echo)のテスト標準ではhandlerを使ってDBなどの外部要素をモック化する例が公式のDOCにも記載があります。ソースコードや公式のDOCを少しずつ読み進めて徐々に理解していきたい。

Discussion