一、前言

  本人負責的支付清結算方向的測試工作,在測試專案中,會出現流程化的介面呼叫,請求完一個介面後,繼續請求另一個介面(這裡的介面可以指Http,也指rpc介面),這裡以一個真實場景為例:使用者在平臺下單,結算前部分退款,再結算,最後結算後部分退款;

  第一個介面動作對應使用者下單,第二個動作對應結算前的部分退款,第三個動作對應結算,第四個動作對應結算後的部分退款,涉及不同系統的互動;這是一個完整的場景,根據我們的測試用例與更多的使用者場景,實際測試中,我們需要測試更多場景,單介面測試已無法滿足實際需求。

二、表格驅動測試

  我們可以定義一個結構體,將每一個步驟定義成一個節點,通過遍歷節點達到執行整個流程的效果:

  優點就是程式碼更加清晰、明確,也便於調整步驟的順序、新增或者移除某些步驟。另外,在迴圈體中增加除錯日誌也非常的簡單;

  但還是有缺點的,看上去似乎不滿足介面測試一些要求,沒有case管理,無法做介面斷言等  

func main() {
ctx := &context{} steps := []struct {
name string
fn func() error
}{
{"parse flags", ctx.parseFlags},
{"read schema", ctx.readSchema},
{"dump schema", ctx.dumpSchema}, // Before transformations
{"remove builtin constructors", ctx.removeBuiltinConstructors},
{"add adhoc constructors", ctx.addAdhocConstructors},
{"validate schema", ctx.validateSchema},
{"decompose arrays", ctx.decomposeArrays},
{"replace arrays", ctx.replaceArrays},
{"resolve generics", ctx.resolveGenerics},
{"dump schema", ctx.dumpSchema}, // After transformations
{"decode combinators", ctx.decodeCombinators},
{"dump decoded combinators", ctx.dumpDecodedCombinators},
{"codegen", ctx.codegen},
} for _, step := range steps {
ctx.debugf("start %s step", step.name)
if err := step.fn(); err != nil {
log.Fatalf("%s: %v", step.name, err)
}
}
}

三、封裝

將場景和節點定義成結構體,提供場景與節點獨立的執行介面:

實際的節點,需要定義成這個結構體的方法;

type SenceSuite struct {
SenceSuite string
} type Plan struct {
Planname string
Fn func(map[string]interface{}) interface{}
Data map[string]interface{}
} var SenceSuiteDao *SenceSuite
var SenceSuiteOnce sync.Once func NewSenceSuiteDao() *SenceSuite {
SenceSuiteOnce.Do(
func() {
SenceSuiteDao = &SenceSuite{}
})
return SenceSuiteDao
} func (dao *SenceSuite) DoSence(steps []Plan) {
for _, step := range steps {
step.Fn(step.Data)
}
} func (dao *SenceSuite) DoPlan(step Plan) interface{} {
return step.Fn(step.Data)
}

  

四、實際使用

介面case管理:"github.com/smartystreets/goconvey/convey"

這裡仍然以上面的場景為例:

//結算前部分退款,再結算,部分退款
func TestRefundAndNomalSettleAndRefund(t *testing.T) {
   // 初始化資料庫
utils.DBInit()
//使用者下單
order := GetOrder("ZFB", "SELF", "nrmol")
env := "prod"
way := "1"
SenceSuite := utils.NewSenceSuiteDao()
convey.Convey("結算前部分退款", t, func() {
P1 := utils.Plan{Planname: "結算前部分退款", Fn: SenceSuite.Refund, Data: map[string]interface{}{}}
res := SenceSuite.DoPlan(P1).(*xxx)
convey.So(res.RetCode, convey.ShouldEqual, "000000")
})
convey.Convey("正常結算", t, func() {
P2 := utils.Plan{Planname: "正常結算", Fn: SenceSuite.Settle, Data: map[string]interface{}{}}
res := SenceSuite.DoPlan(P2).(*xxx)
convey.So(res.RetCode, convey.ShouldEqual, "000000")
})
convey.Convey("結算後部分退款", t, func() {
P3 := utils.Plan{Planname: "結算後部分退款", Fn: SenceSuite.Refund, Data: map[string]interface{}{}}
res := SenceSuite.DoPlan(P3).(*xxx)
convey.So(res.RetCode, convey.ShouldEqual, "000000")
})
}