專案的檔案結構規劃好之後, 就可以依照每支檔案負責的工作填好對應的內容再組裝起來。如果在組裝之前能確定好零件的品質, 結果會不會順利一點呢?
就像組裝模型之前, 可以先把零件一個一個拿出來檢視, 先確定沒有破損或缺漏, 處理好湯口, 這樣組裝的過程或許就能減少突發狀況以及降低因為某個零件需要重新調整而影響進度的狀況。專案的每個零件如果能先個別經過測試, 確保每個零件的輸入輸出都是符合預期的, 這樣在最後整合測試時也能加速問題的排除。
我通常會從核心操作的部分先著手: 像是複雜的計算邏輯或對db、redis的操作就會是我先進行的項目。
這次模擬專案會操作到redis, 在我開始寫程式之前, 我會先用redis-cli
測試預計要使用的語法跟結構, 確定可以做出需要的效果之後再開始寫程式碼。redis 它提供了很多好用的結構, 初步模擬的redis操作只有Set
&Get
, 我會依照操作的行為分別寫好funcion進行測試, 先確定最小操作的function功能沒問題。此外, 如果是會重複的項目, 像是redis key就要另外寫一個統一取得的接口, 避免散落在各處, 遇到要修改時容易漏改。
在redis資料夾中寫好會使用到的操作邏輯
redis
└── limit.go
//(... 略)
// set point
func PointSet(conn *redis.Client, key string, value int, expired time.Duration) (err error) {
_, err = conn.IncrBy(key, int64(value)).Result()
if err != nil {
return
}
_, err = conn.Expire(key, expired).Result()
if err != nil {
return
}
return
}
// get point
func PointGet(conn *redis.Client, key string) (reply int, err error) {
reply, err = conn.Get(key).Int()
if err != nil {
return
}
return
}
寫完之後可以直接寫 go test程式進行邏輯驗證。
redis
├── limit.go
└── limit_test.go
//(... 略)
func TestPoint(t *testing.T) {
conn, err := newRedisConnection()
if err != nil {
t.Error("newRedisConnection Error:", err)
}
sets := &KeySet{
Level1: "AA",
Level2: "B",
Level3: "CCC",
UserName: "Coconut",
}
keys := GetPointKey(sets)
for _, v := range keys {
originPoint, err := PointGet(conn, v)
if err != nil {
if err != redis.Nil {
t.Error("PointGet origin Error:", err)
}
}
err = PointSet(conn, v, 100, time.Duration(30)*time.Second)
if err != nil {
t.Error("PointSet Error:", err)
}
resultPoint, err := PointGet(conn, v)
if err != nil {
t.Error("PointGet result Error:", err)
}
if resultPoint != originPoint+100 {
t.Error("Set point Error")
}
}
}
執行 go test -v -cover=true
=== RUN TestPoint
--- PASS: TestPoint (0.04s)
PASS
coverage: 81.2% of statements
ok github.com/evelynocean/coconut/redis 0.440s
寫完邏輯同時寫好test的優點是:如果之後有異動到邏輯, 只需要重跑 go test
就可以確保輸出有符合預期。go test 的內容可以涵蓋邏輯驗證、計算結果檢查、格式檢查、邊界值檢查...等, 測試寫得越完整就越有保障喔~