前回は、APIテストについて解説しました。
今回は、包括的なテスト方法について解説したいと思います。
<目次>
Ginフレームワークの導入
Go言語のフレームワークであるGinをインストールしましょう、
1 |
go get -u github.com/gin-gonic/gin |
Ginフレームワークによる実装
以下のフォルダとファイルを作成してください。
1 2 3 4 5 6 |
mkdir api/app touch api/app/app.go touch api/app/url_mappings.go mkdir api/controllers touch api/controllers/locations_controller.go touch api/services/locations_service.go |
app.goには、プログラムのスタートトリガーを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// app/app.go package app import ( "github.com/gin-gonic/gin" ) var ( // ルータを作成 router = gin.Default() ) func StartApp() { mapUrls() if err := router.Run(":8080"); err != nil { panic(err) } } |
url_mappings.goには、ユーザーリクエストの処理先を指定します。
1 2 3 4 5 6 7 8 9 |
// app/url_mappings.go package app import "github.com/hoge/golang-testing/api/controllers" func mapUrls() { router.GET("/locations/countries/:country_id", controllers.GetCountry) } |
locations_service.goには、ビジネスロジックを組み込みます。ここではCountry Idを引数に、欲しいCountry情報を取得する処理を書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package services import ( "log" "github.com/hoge/golang-testing/api/domain/locations" "github.com/hoge/golang-testing/api/providers/locations_provider" "github.com/hoge/golang-testing/api/utils/errors" ) func GetCountry(countryID string) (*locations.Country, *errors.ApiError) { return locations_provider.GetCountry(countryID) } |
最後にlocations_controller.goを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// locations_controller.go package controllers import ( "net/http" "github.com/gin-gonic/gin" "github.com/hoge/golang-testing/api/services" ) func GetCountry(c *gin.Context) { country, err := services.GetCountry(c.Param("country_id")) if err != nil { c.JSON(err.Status, err) return } c.JSON(http.StatusOK, country) } |
それでは、main.goに以下の記述をして、プログラムを実行してみましょう。
1 2 3 4 5 6 7 8 9 |
// api/main.go package main import "github.com/hoge/golang-testing/api/app" func main() { app.StartApp() } |
以下のコマンドで、プログラムを実行してください。
1 |
go run main.go |
別のterminalを立ち上げて、curlを実行してみてください。ちなみに、countryIDとして、ARを渡しています。
1 2 3 |
curl "localhost:8080/locations/countries/AR" {"id":"AR","name":"Argentina","time_zone":"GMT-03:00","geo_information":{"location":{"latitude":-38.416096,"longitude":-63.616673}},"states":[{"id":"AR-B","name":"Buenos Aires"},{"id":"AR-C","name":"Capital Federal"},{"id":"AR-K","name":"Catamarca"},{"id":"AR-H","name":"Chaco"},{"id":"AR-U","name":"Chubut"},{"id":"AR-W","name":"Corrientes"},{"id":"AR-X","name":"Córdoba"},{"id":"AR-E","name":"Entre Ríos"},{"id":"AR-P","name":"Formosa"},{"id":"AR-Y","name":"Jujuy"},{"id":"AR-L","name":"La Pampa"},{"id":"AR-F","name":"La Rioja"},{"id":"AR-M","name":"Mendoza"},{"id":"AR-N","name":"Misiones"},{"id":"AR-Q","name":"Neuquén"},{"id":"AR-R","name":"Río Negro"},{"id":"AR-A","name":"Salta"},{"id":"AR-J","name":"San Juan"},{"id":"AR-D","name":"San Luis"},{"id":"AR-Z","name":"Santa Cruz"},{"id":"AR-S","name":"Santa Fe"},{"id":"AR-G","name":"Santiago del Estero"},{"id":"AR-V","name":"Tierra del Fuego"},{"id":"AR-T","name":"Tucumán"}]} |
無事、取得できましたね^^
包含的なテスト
ようやくテストができる状態になりましたので、包含的なテストをしましょう。
包含的なテストってなんだ?と思われているかもしれませんが、単純にこれまで作成した機能を詰め合わせたテストです。
以下のファイルを作成してください。
1 |
touch api/controllers/locations_controller_test.go |
ここには、Ginで作成したControllerと前回解説したMockのテストを組み合わせたテストを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
// locations_controller_test.go package controllers import ( "encoding/json" "net/http" "net/http/httptest" "os" "testing" "github.com/gin-gonic/gin" "github.com/hoge/golang-testing/api/utils/errors" "github.com/stretchr/testify/assert" "github.com/mercadolibre/golang-restclient/rest" ) func TestMain(m *testing.M) { rest.StartMockupServer() os.Exit(m.Run()) } func TestGetCountryNotFound(t *testing.T) { // Init: rest.FlushMockups() rest.AddMockups(&rest.Mock{ URL: "https://api.mercadolibre.com/countries/AR", HTTPMethod: http.MethodGet, RespHTTPCode: http.StatusNotFound, RespBody: `{"message": "Country not found", "error": "not_found", "status": 404, "cause": []}`, }) // Execute: response := httptest.NewRecorder() c, _ := gin.CreateTestContext(response) c.Request, _ = http.NewRequest(http.MethodGet, "", nil) c.Params = gin.Params{ {Key: "country_id", Value: "AR"}, } GetCountry(c) var apiError errors.ApiError err := json.Unmarshal(response.Body.Bytes(), &apiError) // Validation: assert.EqualValues(t, http.StatusNotFound, response.Code) assert.Nil(t, err) assert.EqualValues(t, http.StatusNotFound, apiError.Status) assert.EqualValues(t, "Country not found", apiError.Message) } |
テストを実行してみましょう。
1 2 3 4 5 6 7 8 |
$ go test -run TestGetCountryNotFound [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) PASS ok github.com/hoge/golang-testing/api/controllers 0.039s |
Passしました!
終わりに
Go言語に限らず、プログラミングではテストコードを書くことがもはや当たり前の時代になりつつあります。
私も現場でテストコードを書いていますが、まだまだ適切なテストコードがかけているわけではありません。
日々、改善点を見つけ、より良いテストコードをかけるように奮闘中です。
特にMockが難しく感じていますね。
テストコードを書くことは難しいですが、道具は使い込めば使い込むほど、手に馴染んでいくものです。
もっとテストコードを書いて、慣れていけばいいと思います。
それでは、また!
コメントを残す
コメントを投稿するにはログインしてください。