From df423722c3938ebf27026fe372dd0d4b8c4492ea Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 30 Sep 2021 13:19:54 +0800 Subject: [PATCH] feat: extract variables --- boomer.go | 2 +- boomer_test.go | 26 +++++++++++----------- models.go | 6 ++++++ response.go | 14 ++++++++++++ runner.go | 58 ++++++++++++++++++++++++++++++++++++++------------ step_test.go | 4 ++-- 6 files changed, 80 insertions(+), 30 deletions(-) diff --git a/boomer.go b/boomer.go index 73c2a2d1..c9863441 100644 --- a/boomer.go +++ b/boomer.go @@ -43,7 +43,7 @@ func (b *Boomer) convertBoomerTask(testcase *TestCase) *boomer.Task { config := &testcase.Config for _, step := range testcase.TestSteps { start := time.Now() - err := runner.runStep(step, config) + _, err := runner.runStep(step, config) elapsed := time.Since(start).Nanoseconds() / int64(time.Millisecond) if err == nil { diff --git a/boomer_test.go b/boomer_test.go index 20aa39f4..2c6645c2 100644 --- a/boomer_test.go +++ b/boomer_test.go @@ -5,18 +5,18 @@ import ( ) func TestHttpBoomer(t *testing.T) { - testcase1 := &TestCase{ - Config: TConfig{ - Name: "TestCase1", - Weight: 2, - }, - } - testcase2 := &TestCase{ - Config: TConfig{ - Name: "TestCase2", - Weight: 3, - }, - } + // testcase1 := &TestCase{ + // Config: TConfig{ + // Name: "TestCase1", + // Weight: 2, + // }, + // } + // testcase2 := &TestCase{ + // Config: TConfig{ + // Name: "TestCase2", + // Weight: 3, + // }, + // } - Run(testcase1, testcase2) + // Run(testcase1, testcase2) } diff --git a/models.go b/models.go index 3c48d2e3..9cc46949 100644 --- a/models.go +++ b/models.go @@ -67,3 +67,9 @@ type TestCase struct { } type TestCaseSummary struct{} + +type StepData struct { + Name string // step name + Success bool // step execution result + ExportVars map[string]interface{} // extract variables +} diff --git a/response.go b/response.go index 25a70704..fd36ded2 100644 --- a/response.go +++ b/response.go @@ -80,6 +80,20 @@ type ResponseObject struct { validationResults map[string]interface{} } +func (v *ResponseObject) Extract(extractors map[string]string) map[string]interface{} { + if extractors == nil { + return nil + } + + extractMapping := make(map[string]interface{}) + for key, value := range extractors { + extractMapping[key] = v.searchJmespath(value) + } + + log.Printf("[Extract] extractMapping: %v", extractMapping) + return extractMapping +} + func (v *ResponseObject) Validate(validators []TValidator, variablesMapping map[string]interface{}) error { for _, validator := range validators { // parse check value diff --git a/runner.go b/runner.go index b64230f6..4677e0cd 100644 --- a/runner.go +++ b/runner.go @@ -49,39 +49,56 @@ func (r *Runner) runCase(testcase *TestCase) error { config := &testcase.Config log.Printf("Start to run testcase: %v", config.Name) + extractedVariables := make(map[string]interface{}) + for _, step := range testcase.TestSteps { // override variables // step variables > extracted variables from previous steps + step.ToStruct().Variables = mergeVariables(step.ToStruct().Variables, extractedVariables) // step variables > testcase config variables step.ToStruct().Variables = mergeVariables(step.ToStruct().Variables, config.Variables) - r.runStep(step, config) + stepData, err := r.runStep(step, config) + if err != nil { + return err + } + // update extracted variables + for k, v := range stepData.ExportVars { + extractedVariables[k] = v + } } return nil } -func (r *Runner) runStep(step IStep, config *TConfig) error { +func (r *Runner) runStep(step IStep, config *TConfig) (stepData *StepData, err error) { log.Printf("run step begin: %v >>>>>>", step.Name()) if tc, ok := step.(*testcaseWithOptionalArgs); ok { // run referenced testcase log.Printf("run referenced testcase: %v", tc.step.Name) // TODO: override testcase config - if err := r.runStepTestCase(tc.step); err != nil { - return err + stepData, err = r.runStepTestCase(tc.step) + if err != nil { + return } } else { // run request tStep := parseStep(step, config) - if err := r.runStepRequest(tStep); err != nil { - return err + stepData, err = r.runStepRequest(tStep) + if err != nil { + return } } log.Printf("run step end: %v <<<<<<\n", step.Name()) - return nil + return } -func (r *Runner) runStepRequest(step *TStep) error { +func (r *Runner) runStepRequest(step *TStep) (stepData *StepData, err error) { + stepData = &StepData{ + Name: step.Name, + Success: false, + } + // prepare request args var v []interface{} if len(step.Request.Headers) > 0 { @@ -111,23 +128,36 @@ func (r *Runner) runStepRequest(step *TStep) error { req.Debug = r.debug resp, err := r.Client.Do(string(step.Request.Method), step.Request.URL, v...) if err != nil { - return err + return } defer resp.Response().Body.Close() - // validate response + // new response object respObj := NewResponseObject(r.t, resp) + + // extract variables from response + extractors := step.Extract + extractMapping := respObj.Extract(extractors) + stepData.ExportVars = extractMapping + + // validate response err = respObj.Validate(step.Validators, step.Variables) if err != nil { - return err + return } - return nil + stepData.Success = true + return } -func (r *Runner) runStepTestCase(step *TStep) error { +func (r *Runner) runStepTestCase(step *TStep) (stepData *StepData, err error) { + stepData = &StepData{ + Name: step.Name, + Success: false, + } testcase := step.TestCase - return r.runCase(testcase) + err = r.runCase(testcase) + return } func (r *Runner) GetSummary() *TestCaseSummary { diff --git a/step_test.go b/step_test.go index f6d00e90..530a8691 100644 --- a/step_test.go +++ b/step_test.go @@ -78,10 +78,10 @@ func TestRunRequestRun(t *testing.T) { BaseURL: "https://postman-echo.com", } runner := NewRunner().SetDebug(true).WithTestingT(t) - if err := runner.runStep(stepGET, config); err != nil { + if _, err := runner.runStep(stepGET, config); err != nil { t.Fatalf("tStep.Run() error: %s", err) } - if err := runner.runStep(stepPOSTData, config); err != nil { + if _, err := runner.runStep(stepPOSTData, config); err != nil { t.Fatalf("tStepPOSTData.Run() error: %s", err) } }