iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0

What is API Test?

我們可以把它想成Unit Test單元測試的一種,不過它所涵蓋的最好集合不像以往的UnitTest可能以Function為主,而是Endpoint

透過API Test我們能夠在開發與修改程式碼時,確保被修改到部分程式碼的API能夠如預期的運作,那這邊我們會以官方提供的httptest來實作。

https://ithelp.ithome.com.tw/upload/images/20211010/20129737oKVtf3CBHN.png

API Test with httptest

首先我們將main當中的router部分給分離出來

main.go

package main

import (
	"fmt"
	"github.com/chenyahui/gin-cache/persist"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"github.com/joho/godotenv"
	ginSwagger "github.com/swaggo/gin-swagger"
	"github.com/swaggo/gin-swagger/swaggerFiles"
	"ironman-2021/app/config"
	"ironman-2021/app/dao"
	"ironman-2021/app/middleware"
	"ironman-2021/app/model"
	"net/http"
	"os"
)

// @title Gin swagger
// @version 1.0
// @description Gin swagger

// @contact.name Flynn Sun

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:8080
// schemes http
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
func main() {
	envErr := godotenv.Load()
	if envErr != nil {
		panic(envErr)
	}

	port := os.Getenv("PORT")
	server := SetRouter()
	err := server.Run(":" + port)
	if err != nil {
		panic(err)
	}
}

func SetRouter() *gin.Engine {
	envErr := godotenv.Load()
	if envErr != nil {
		panic(envErr)
	}

	dbConfig := os.Getenv("DB_CONFIG")
	db, ormErr := dao.Initialize(dbConfig)
	if ormErr != nil {
		panic(ormErr)
	}
	migrateErr := db.AutoMigrate(&model.User{})
	if migrateErr != nil {
		return nil
	}

	server := gin.Default()
	server.Use(middleware.CORSMiddleware())
	server.GET("/hc", func(c *gin.Context) {
		c.String(http.StatusOK, fmt.Sprintf("Health Check"))
	})
	redisStore := persist.NewRedisStore(redis.NewClient(&redis.Options{
		Network: "tcp",
		Addr:    "redis:6379",
		DB:      0,
	}))
	config.RouteUsers(server, redisStore)
	url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
	server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
	return server
}

接著則在專案的根目錄加上一個main_test.go的檔案

main_test.go

package main

import (
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"testing"
)

func Test_setupRouter(t *testing.T) {
	router := SetRouter()

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/hc", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "Health Check")
}
  • 首先一樣創建一個與main相同的router
  • 接著Create 一個ResponseRecorder的物件
  • 再來則將Request /hc的Response給conform到ResponseRecorder
  • 然後比較Response Code、Response Text等內容是否符合預期

Run in Container

  • 首先進入ironman-2021這個container
docker exec -it ironman-2021 bash                                                                 4280  16:27:57 
root@88c2c60ca49b:/usr/local/go/src/ironman-2021# ls
  • 接著輸入go test -v的command執行單元測試
go test -v
=== RUN   Test_setupRouter
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[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)

[GIN-debug] GET    /hc                       --> ironman-2021.SetRouter.func1 (4 handlers)
[GIN-debug] POST   /v1/users/                --> ironman-2021/app/controller.UsersController.CreateUser-fm (4 handlers)
[GIN-debug] GET    /v1/users/:id             --> ironman-2021/app/controller.UsersController.GetUser-fm (6 handlers)
[GIN-debug] POST   /v1/users/login           --> ironman-2021/app/controller.UsersController.AuthHandler-fm (4 handlers)
[GIN-debug] GET    /swagger/*any             --> github.com/swaggo/gin-swagger.CustomWrapHandler.func1 (4 handlers)
[GIN] 2021/10/08 - 08:29:40 | 200 |       107.8µs |                 | GET      "/hc"
--- PASS: Test_setupRouter (0.06s)
PASS
ok      ironman-2021    0.100s

那從這我們就可以看到他執行單元測試的結果與耗費時間等資料,用以確認是否程式碼如預期地運作!

More Test!

再來我們就可以依序寫更多的Test Code,從Create a new userLogin with an existing user

package main

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"testing"
)

func Test_setupRouter(t *testing.T) {
	router := SetRouter()

	w := httptest.NewRecorder()
	req1, _ := http.NewRequest("GET", "/hc", nil)
	router.ServeHTTP(w, req1)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "Health Check")

	var jsonStr1 = []byte(`{"account":"account","password":"password", "email":"test123@gmail.com"}`)
	req2, _ := http.NewRequest("POST", "/v1/users/", bytes.NewBuffer(jsonStr1))

	router.ServeHTTP(w, req2)
	assert.Equal(t, http.StatusOK, w.Code)

	var jsonStr2 = []byte(`{"account":"account","password":"password"`)
	req3, _:= http.NewRequest("POST", "/v1/users/login/", bytes.NewBuffer(jsonStr2))
	router.ServeHTTP(w, req3)
	assert.Equal(t, http.StatusOK, w.Code)
}

Summary

這章節主要介紹如何使用官方的httptest來寫Http Test,希望能降低修改程式碼時出錯的概率!

相關的程式碼一樣會放在下方連結。

https://github.com/Neskem/Ironman-2021/tree/Day-25


上一篇
Day24 Gin with Cache
下一篇
Day26 Gin with Logger
系列文
fmt.Println("從零開始的Golang生活")30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言