Go測試技術分享(一):場景化介面Case編寫

發表於2021-08-07

 

 

一、前言

  本人負責的支付清結算方向的測試工作,在測試專案中,會出現流程化的介面呼叫,請求完一個介面後,繼續請求另一個介面(這裡的介面可以指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")
	})
}

  

  

 

 

 

 

 

 

  

相關文章