From 26c4f5c0c34130cb990852c8a2a6ba46d2953bd9 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 30 Mar 2022 12:06:16 +0800 Subject: [PATCH] refactor: TestCaseSessionRunner --- hrp/boomer.go | 3 +- hrp/parser.go | 70 ++++++++++------------------------- hrp/parser_test.go | 26 ++++++------- hrp/response.go | 8 ++-- hrp/runner.go | 5 ++- hrp/session.go | 87 ++++++++++++++++++++++++-------------------- hrp/step_api.go | 20 ++++++++++ hrp/step_request.go | 81 ++++++++++++++++++++++------------------- hrp/step_testcase.go | 31 +++++++++++----- 9 files changed, 173 insertions(+), 158 deletions(-) diff --git a/hrp/boomer.go b/hrp/boomer.go index b710a640..f55617bd 100644 --- a/hrp/boomer.go +++ b/hrp/boomer.go @@ -96,7 +96,6 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend Weight: config.Weight, Fn: func() { sessionRunner := hrpRunner.NewSessionRunner(testcase) - sessionRunner.init() sessionRunner.parser.plugin = plugin testcaseSuccess := true // flag whole testcase result @@ -136,7 +135,7 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend testcaseSuccess = false transactionSuccess = false - if sessionRunner.hrpRunner.failfast { + if hrpRunner.failfast { log.Error().Msg("abort running due to failfast setting") break } diff --git a/hrp/parser.go b/hrp/parser.go index cf81ed2c..4a8126a3 100644 --- a/hrp/parser.go +++ b/hrp/parser.go @@ -17,11 +17,11 @@ import ( "github.com/httprunner/httprunner/hrp/internal/builtin" ) -func newParser() *parser { - return &parser{} +func newParser() *Parser { + return &Parser{} } -type parser struct { +type Parser struct { plugin funplugin.IPlugin // plugin is used to call functions } @@ -42,9 +42,9 @@ func buildURL(baseURL, stepURL string) string { return uStep.String() } -func (p *parser) parseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) (map[string]string, error) { +func (p *Parser) ParseHeaders(rawHeaders map[string]string, variablesMapping map[string]interface{}) (map[string]string, error) { parsedHeaders := make(map[string]string) - headers, err := p.parseData(rawHeaders, variablesMapping) + headers, err := p.Parse(rawHeaders, variablesMapping) if err != nil { return rawHeaders, err } @@ -64,7 +64,7 @@ func convertString(raw interface{}) string { } } -func (p *parser) parseData(raw interface{}, variablesMapping map[string]interface{}) (interface{}, error) { +func (p *Parser) Parse(raw interface{}, variablesMapping map[string]interface{}) (interface{}, error) { rawValue := reflect.ValueOf(raw) switch rawValue.Kind() { case reflect.String: @@ -75,11 +75,11 @@ func (p *parser) parseData(raw interface{}, variablesMapping map[string]interfac // other string value := rawValue.String() value = strings.TrimSpace(value) - return p.parseString(value, variablesMapping) + return p.ParseString(value, variablesMapping) case reflect.Slice: parsedSlice := make([]interface{}, rawValue.Len()) for i := 0; i < rawValue.Len(); i++ { - parsedValue, err := p.parseData(rawValue.Index(i).Interface(), variablesMapping) + parsedValue, err := p.Parse(rawValue.Index(i).Interface(), variablesMapping) if err != nil { return raw, err } @@ -89,12 +89,12 @@ func (p *parser) parseData(raw interface{}, variablesMapping map[string]interfac case reflect.Map: // convert any map to map[string]interface{} parsedMap := make(map[string]interface{}) for _, k := range rawValue.MapKeys() { - parsedKey, err := p.parseString(k.String(), variablesMapping) + parsedKey, err := p.ParseString(k.String(), variablesMapping) if err != nil { return raw, err } v := rawValue.MapIndex(k) - parsedValue, err := p.parseData(v.Interface(), variablesMapping) + parsedValue, err := p.Parse(v.Interface(), variablesMapping) if err != nil { return raw, err } @@ -131,8 +131,8 @@ var ( regexCompileNumber = regexp.MustCompile(regexNumber) // parse number ) -// parseString parse string with variables -func (p *parser) parseString(raw string, variablesMapping map[string]interface{}) (interface{}, error) { +// ParseString parse string with variables +func (p *Parser) ParseString(raw string, variablesMapping map[string]interface{}) (interface{}, error) { matchStartPosition := 0 parsedString := "" remainedString := raw @@ -171,12 +171,12 @@ func (p *parser) parseString(raw string, variablesMapping map[string]interface{} if err != nil { return raw, err } - parsedArgs, err := p.parseData(arguments, variablesMapping) + parsedArgs, err := p.Parse(arguments, variablesMapping) if err != nil { return raw, err } - result, err := p.callFunc(funcName, parsedArgs.([]interface{})...) + result, err := p.CallFunc(funcName, parsedArgs.([]interface{})...) if err != nil { log.Error().Str("funcName", funcName).Interface("arguments", arguments). Err(err).Msg("call function failed") @@ -237,9 +237,9 @@ func (p *parser) parseString(raw string, variablesMapping map[string]interface{} return parsedString, nil } -// callFunc calls function with arguments +// CallFunc calls function with arguments // only support return at most one result value -func (p *parser) callFunc(funcName string, arguments ...interface{}) (interface{}, error) { +func (p *Parser) CallFunc(funcName string, arguments ...interface{}) (interface{}, error) { // call with plugin function if p.plugin != nil && p.plugin.Has(funcName) { return p.plugin.Call(funcName, arguments...) @@ -342,38 +342,6 @@ func mergeSlices(slice, overriddenSlice []string) []string { return slice } -// extend teststep with api, teststep will merge and override referenced api -func extendWithAPI(testStep *TStep, overriddenStep *API) { - // override api name - if testStep.Name == "" { - testStep.Name = overriddenStep.Name - } - // merge & override request - testStep.Request = overriddenStep.Request - // merge & override variables - testStep.Variables = mergeVariables(testStep.Variables, overriddenStep.Variables) - // merge & override extractors - testStep.Extract = mergeMap(testStep.Extract, overriddenStep.Extract) - // merge & override validators - testStep.Validators = mergeValidators(testStep.Validators, overriddenStep.Validators) - // merge & override setupHooks - testStep.SetupHooks = mergeSlices(testStep.SetupHooks, overriddenStep.SetupHooks) - // merge & override teardownHooks - testStep.TeardownHooks = mergeSlices(testStep.TeardownHooks, overriddenStep.TeardownHooks) -} - -// extend referenced testcase with teststep, teststep config merge and override referenced testcase config -func extendWithTestCase(testStep *TStep, overriddenTestCase *TestCase) { - // override testcase name - if testStep.Name != "" { - overriddenTestCase.Config.Name = testStep.Name - } - // merge & override variables - overriddenTestCase.Config.Variables = mergeVariables(testStep.Variables, overriddenTestCase.Config.Variables) - // merge & override extractors - overriddenTestCase.Config.Export = mergeSlices(testStep.Export, overriddenTestCase.Config.Export) -} - var eval = goval.NewEvaluator() // literalEval parse string to number if possible @@ -420,7 +388,7 @@ func parseFunctionArguments(argsStr string) ([]interface{}, error) { return arguments, nil } -func (p *parser) parseVariables(variables map[string]interface{}) (map[string]interface{}, error) { +func (p *Parser) ParseVariables(variables map[string]interface{}) (map[string]interface{}, error) { parsedVariables := make(map[string]interface{}) var traverseRounds int @@ -458,7 +426,7 @@ func (p *parser) parseVariables(variables map[string]interface{}) (map[string]in return variables, fmt.Errorf("variable not defined: %v", undefinedVars) } - parsedValue, err := p.parseData(varValue, parsedVariables) + parsedValue, err := p.Parse(varValue, parsedVariables) if err != nil { continue } @@ -588,7 +556,7 @@ func parseParameters(parameters map[string]interface{}, variablesMapping map[str case reflect.String: // e.g. username-password: ${parameterize(examples/hrp/account.csv)} -> [{"username": "test1", "password": "111111"}, {"username": "test2", "password": "222222"}] var parsedParameterContent interface{} - parsedParameterContent, err = newParser().parseString(rawValue.String(), variablesMapping) + parsedParameterContent, err = newParser().ParseString(rawValue.String(), variablesMapping) if err != nil { log.Error().Interface("parameterContent", rawValue).Msg("[parseParameters] parse parameter content error") return nil, err diff --git a/hrp/parser_test.go b/hrp/parser_test.go index 3ad0a2b4..64ba38d4 100644 --- a/hrp/parser_test.go +++ b/hrp/parser_test.go @@ -163,7 +163,7 @@ func TestParseDataStringWithVariables(t *testing.T) { parser := newParser() for _, data := range testData { - parsedData, err := parser.parseData(data.expr, variablesMapping) + parsedData, err := parser.Parse(data.expr, variablesMapping) if !assert.NoError(t, err) { t.Fail() } @@ -188,7 +188,7 @@ func TestParseDataStringWithUndefinedVariables(t *testing.T) { parser := newParser() for _, data := range testData { - parsedData, err := parser.parseData(data.expr, variablesMapping) + parsedData, err := parser.Parse(data.expr, variablesMapping) if !assert.Error(t, err) { t.Fail() } @@ -233,7 +233,7 @@ func TestParseDataStringWithVariablesAbnormal(t *testing.T) { parser := newParser() for _, data := range testData { - parsedData, err := parser.parseData(data.expr, variablesMapping) + parsedData, err := parser.Parse(data.expr, variablesMapping) if !assert.NoError(t, err) { t.Fail() } @@ -264,7 +264,7 @@ func TestParseDataMapWithVariables(t *testing.T) { parser := newParser() for _, data := range testData { - parsedData, err := parser.parseData(data.expr, variablesMapping) + parsedData, err := parser.Parse(data.expr, variablesMapping) if !assert.NoError(t, err) { t.Fail() } @@ -298,7 +298,7 @@ func TestParseHeaders(t *testing.T) { parser := newParser() for _, data := range testData { - parsedHeaders, err := parser.parseHeaders(data.rawHeaders, variablesMapping) + parsedHeaders, err := parser.ParseHeaders(data.rawHeaders, variablesMapping) if !assert.NoError(t, err) { t.Fail() } @@ -444,14 +444,14 @@ func TestCallBuiltinFunction(t *testing.T) { parser := newParser() // call function without arguments - _, err := parser.callFunc("get_timestamp") + _, err := parser.CallFunc("get_timestamp") if !assert.NoError(t, err) { t.Fail() } // call function with one argument timeStart := time.Now() - _, err = parser.callFunc("sleep", 1) + _, err = parser.CallFunc("sleep", 1) if !assert.NoError(t, err) { t.Fail() } @@ -460,7 +460,7 @@ func TestCallBuiltinFunction(t *testing.T) { } // call function with one argument - result, err := parser.callFunc("gen_random_string", 10) + result, err := parser.CallFunc("gen_random_string", 10) if !assert.NoError(t, err) { t.Fail() } @@ -469,7 +469,7 @@ func TestCallBuiltinFunction(t *testing.T) { } // call function with two argument - result, err = parser.callFunc("max", float64(10), 9.99) + result, err = parser.CallFunc("max", float64(10), 9.99) if !assert.NoError(t, err) { t.Fail() } @@ -556,7 +556,7 @@ func TestParseDataStringWithFunctions(t *testing.T) { parser := newParser() for _, data := range testData1 { - value, err := parser.parseData(data.expr, variablesMapping) + value, err := parser.Parse(data.expr, variablesMapping) if !assert.NoError(t, err) { t.Fail() } @@ -575,7 +575,7 @@ func TestParseDataStringWithFunctions(t *testing.T) { } for _, data := range testData2 { - value, err := parser.parseData(data.expr, variablesMapping) + value, err := parser.Parse(data.expr, variablesMapping) if !assert.NoError(t, err) { t.Fail() } @@ -623,7 +623,7 @@ func TestParseVariables(t *testing.T) { parser := newParser() for _, data := range testData { - value, err := parser.parseVariables(data.rawVars) + value, err := parser.ParseVariables(data.rawVars) if !assert.NoError(t, err) { t.Fail() } @@ -654,7 +654,7 @@ func TestParseVariablesAbnormal(t *testing.T) { parser := newParser() for _, data := range testData { - value, err := parser.parseVariables(data.rawVars) + value, err := parser.ParseVariables(data.rawVars) if !assert.Error(t, err) { t.Fail() } diff --git a/hrp/response.go b/hrp/response.go index e6328806..37164eb0 100644 --- a/hrp/response.go +++ b/hrp/response.go @@ -18,7 +18,7 @@ import ( "github.com/httprunner/httprunner/hrp/internal/json" ) -func newResponseObject(t *testing.T, parser *parser, resp *http.Response) (*responseObject, error) { +func newResponseObject(t *testing.T, parser *Parser, resp *http.Response) (*responseObject, error) { // prepare response headers headers := make(map[string]string) for k, v := range resp.Header { @@ -82,7 +82,7 @@ type respObjMeta struct { type responseObject struct { t *testing.T - parser *parser + parser *Parser respObjMeta interface{} validationResults []*ValidationResult } @@ -126,7 +126,7 @@ func (v *responseObject) Validate(iValidators []interface{}, variablesMapping ma var checkValue interface{} if strings.Contains(checkItem, "$") { // reference variable - checkValue, err = v.parser.parseData(checkItem, variablesMapping) + checkValue, err = v.parser.Parse(checkItem, variablesMapping) if err != nil { return err } @@ -143,7 +143,7 @@ func (v *responseObject) Validate(iValidators []interface{}, variablesMapping ma } // parse expected value - expectValue, err := v.parser.parseData(validator.Expect, variablesMapping) + expectValue, err := v.parser.Parse(validator.Expect, variablesMapping) if err != nil { return err } diff --git a/hrp/runner.go b/hrp/runner.go index e7505887..6847d9fd 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -151,11 +151,11 @@ func (r *HRPRunner) Run(testcases ...ITestCase) error { } } sessionRunner := r.NewSessionRunner(testcase) - if err = sessionRunner.Run(); err != nil { + if err = sessionRunner.Start(); err != nil { log.Error().Err(err).Msg("[Run] run testcase failed") return err } - caseSummary := sessionRunner.getSummary() + caseSummary := sessionRunner.GetSummary() s.appendCaseSummary(caseSummary) } } @@ -191,5 +191,6 @@ func (r *HRPRunner) NewSessionRunner(testcase *TestCase) *SessionRunner { parser: newParser(), summary: newSummary(), } + sessionRunner.init() return sessionRunner } diff --git a/hrp/session.go b/hrp/session.go index 84e293e5..b65f92d4 100644 --- a/hrp/session.go +++ b/hrp/session.go @@ -4,7 +4,6 @@ import ( _ "embed" "time" - "github.com/jinzhu/copier" "github.com/pkg/errors" "github.com/rs/zerolog/log" ) @@ -14,7 +13,7 @@ import ( type SessionRunner struct { testCase *TestCase hrpRunner *HRPRunner - parser *parser + parser *Parser sessionVariables map[string]interface{} // transactions stores transaction timing info. // key is transaction name, value is map of transaction type and time, e.g. start time and end time. @@ -31,8 +30,20 @@ func (r *SessionRunner) init() { r.summary.Name = r.testCase.Config.Name } -// Run runs the test steps in sequential order. -func (r *SessionRunner) Run() error { +func (r *SessionRunner) GetParser() *Parser { + return r.parser +} + +func (r *SessionRunner) GetConfig() *TConfig { + return r.testCase.Config +} + +func (r *SessionRunner) LogOn() bool { + return r.hrpRunner.requestsLogOn +} + +// Start runs the test steps in sequential order. +func (r *SessionRunner) Start() error { config := r.testCase.Config log.Info().Str("testcase", config.Name).Msg("run testcase start") @@ -68,48 +79,46 @@ func (r *SessionRunner) Run() error { return nil } -func (r *SessionRunner) overrideVariables(step *TStep) (*TStep, error) { - // copy step and config to avoid data racing - copiedStep := &TStep{} - if err := copier.Copy(copiedStep, step); err != nil { - log.Error().Err(err).Msg("copy step data failed") - return nil, err +func (r *SessionRunner) UpdateSession(vars map[string]interface{}) { + for k, v := range vars { + r.sessionVariables[k] = v } - - stepVariables := copiedStep.Variables - // override variables - // step variables > session variables (extracted variables from previous steps) - stepVariables = mergeVariables(stepVariables, r.sessionVariables) - // step variables > testcase config variables - stepVariables = mergeVariables(stepVariables, r.testCase.Config.Variables) - - // parse step variables - parsedVariables, err := r.parser.parseVariables(stepVariables) - if err != nil { - log.Error().Interface("variables", r.testCase.Config.Variables).Err(err).Msg("parse step variables failed") - return nil, err - } - copiedStep.Variables = parsedVariables // avoid data racing - return copiedStep, nil } -func (r *SessionRunner) overrideConfig(step *TStep) { - // override headers - if r.testCase.Config.Headers != nil { - step.Request.Headers = mergeMap(step.Request.Headers, r.testCase.Config.Headers) +// UpdateSummary appends step result to summary +func (r *SessionRunner) UpdateSummary(stepResult *StepResult) { + r.summary.Records = append(r.summary.Records, stepResult) + r.summary.Stat.Total += 1 + if stepResult.Success { + r.summary.Stat.Successes += 1 + } else { + r.summary.Stat.Failures += 1 + // update summary result to failed + r.summary.Success = false } - // parse step request url - requestUrl, err := r.parser.parseString(step.Request.URL, step.Variables) +} + +// MergeStepVariables merges step variables with config variables and session variables +func (r *SessionRunner) MergeStepVariables(vars map[string]interface{}) (map[string]interface{}, error) { + // override variables + // step variables > session variables (extracted variables from previous steps) + overrideVars := mergeVariables(vars, r.sessionVariables) + // step variables > testcase config variables + overrideVars = mergeVariables(overrideVars, r.testCase.Config.Variables) + + // parse step variables + parsedVariables, err := r.parser.ParseVariables(overrideVars) if err != nil { - log.Error().Err(err).Msg("parse request url failed") - requestUrl = step.Variables + log.Error().Interface("variables", r.testCase.Config.Variables). + Err(err).Msg("parse step variables failed") + return nil, err } - step.Request.URL = buildURL(r.testCase.Config.BaseURL, convertString(requestUrl)) // avoid data racing + return parsedVariables, nil } func (r *SessionRunner) parseConfig(cfg *TConfig) error { // parse config variables - parsedVariables, err := r.parser.parseVariables(cfg.Variables) + parsedVariables, err := r.parser.ParseVariables(cfg.Variables) if err != nil { log.Error().Interface("variables", cfg.Variables).Err(err).Msg("parse config variables failed") return err @@ -117,14 +126,14 @@ func (r *SessionRunner) parseConfig(cfg *TConfig) error { cfg.Variables = parsedVariables // parse config name - parsedName, err := r.parser.parseString(cfg.Name, cfg.Variables) + parsedName, err := r.parser.ParseString(cfg.Name, cfg.Variables) if err != nil { return err } cfg.Name = convertString(parsedName) // parse config base url - parsedBaseURL, err := r.parser.parseString(cfg.BaseURL, cfg.Variables) + parsedBaseURL, err := r.parser.ParseString(cfg.BaseURL, cfg.Variables) if err != nil { return err } @@ -136,7 +145,7 @@ func (r *SessionRunner) parseConfig(cfg *TConfig) error { return nil } -func (r *SessionRunner) getSummary() *TestCaseSummary { +func (r *SessionRunner) GetSummary() *TestCaseSummary { caseSummary := r.summary caseSummary.Time.StartAt = r.startTime caseSummary.Time.Duration = time.Since(r.startTime).Seconds() diff --git a/hrp/step_api.go b/hrp/step_api.go index 04397129..c275cea8 100644 --- a/hrp/step_api.go +++ b/hrp/step_api.go @@ -107,3 +107,23 @@ func (s *StepAPIWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error) { stepResult.StepType = stepTypeAPI return stepResult, nil } + +// extend teststep with api, teststep will merge and override referenced api +func extendWithAPI(testStep *TStep, overriddenStep *API) { + // override api name + if testStep.Name == "" { + testStep.Name = overriddenStep.Name + } + // merge & override request + testStep.Request = overriddenStep.Request + // merge & override variables + testStep.Variables = mergeVariables(testStep.Variables, overriddenStep.Variables) + // merge & override extractors + testStep.Extract = mergeMap(testStep.Extract, overriddenStep.Extract) + // merge & override validators + testStep.Validators = mergeValidators(testStep.Validators, overriddenStep.Validators) + // merge & override setupHooks + testStep.SetupHooks = mergeSlices(testStep.SetupHooks, overriddenStep.SetupHooks) + // merge & override teardownHooks + testStep.TeardownHooks = mergeSlices(testStep.TeardownHooks, overriddenStep.TeardownHooks) +} diff --git a/hrp/step_request.go b/hrp/step_request.go index f3560365..ed026e0f 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -51,42 +51,55 @@ type Request struct { } func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) { - - step, err = r.overrideVariables(step) - if err != nil { - return nil, err - } - r.overrideConfig(step) - log.Info().Str("step", step.Name).Msg("run step start") + stepResult = &StepResult{ Name: step.Name, StepType: stepTypeRequest, Success: false, ContentSize: 0, } - sessionData := newSessionData() defer func() { + // update testcase summary if err != nil { log.Error().Err(err).Msg("run request step failed") stepResult.Attachment = err.Error() - r.summary.Success = false } else { + // update extracted variables + r.UpdateSession(stepResult.ExportVars) log.Info(). Str("step", step.Name). Bool("success", stepResult.Success). Interface("exportVars", stepResult.ExportVars). Msg("run step end") } + r.UpdateSummary(stepResult) }() + sessionData := newSessionData() + parser := r.GetParser() + config := r.GetConfig() + + // override step variables + stepVariables, err := r.MergeStepVariables(step.Variables) + if err != nil { + return + } + // convert request struct to map jsonRequest, _ := json.Marshal(&step.Request) var requestMap map[string]interface{} _ = json.Unmarshal(jsonRequest, &requestMap) - rawUrl := step.Request.URL + // parse step request url + requestUrl, err := r.parser.ParseString(step.Request.URL, stepVariables) + if err != nil { + log.Error().Err(err).Msg("parse request url failed") + return + } + rawUrl := buildURL(r.testCase.Config.BaseURL, convertString(requestUrl)) + method := step.Request.Method req := &http.Request{ Method: string(method), @@ -97,8 +110,14 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err } // prepare request headers - if len(step.Request.Headers) > 0 { - headers, err := r.parser.parseHeaders(step.Request.Headers, step.Variables) + stepHeaders := step.Request.Headers + if config.Headers != nil { + // override headers + stepHeaders = mergeMap(stepHeaders, config.Headers) + } + + if len(stepHeaders) > 0 { + headers, err := parser.ParseHeaders(stepHeaders, stepVariables) if err != nil { return stepResult, errors.Wrap(err, "parse headers failed") } @@ -121,7 +140,7 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err // prepare request params var queryParams url.Values if len(step.Request.Params) > 0 { - params, err := r.parser.parseData(step.Request.Params, step.Variables) + params, err := parser.Parse(step.Request.Params, stepVariables) if err != nil { return stepResult, errors.Wrap(err, "parse request params failed") } @@ -146,7 +165,7 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err // prepare request cookies for cookieName, cookieValue := range step.Request.Cookies { - value, err := r.parser.parseData(cookieValue, step.Variables) + value, err := parser.Parse(cookieValue, stepVariables) if err != nil { return stepResult, errors.Wrap(err, "parse cookie value failed") } @@ -158,7 +177,7 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err // prepare request body if step.Request.Body != nil { - data, err := r.parser.parseData(step.Request.Body, step.Variables) + data, err := parser.Parse(step.Request.Body, stepVariables) if err != nil { return stepResult, err } @@ -232,19 +251,19 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err req.Host = u.Host // add request object to step variables, could be used in setup hooks - step.Variables["hrp_step_name"] = step.Name - step.Variables["hrp_step_request"] = requestMap + stepVariables["hrp_step_name"] = step.Name + stepVariables["hrp_step_request"] = requestMap // deal with setup hooks for _, setupHook := range step.SetupHooks { - _, err = r.parser.parseData(setupHook, step.Variables) + _, err = parser.Parse(setupHook, stepVariables) if err != nil { return stepResult, errors.Wrap(err, "run setup hooks failed") } } // log & print request - if r.hrpRunner.requestsLogOn { + if r.LogOn() { if err := printRequest(req); err != nil { return stepResult, err } @@ -266,25 +285,25 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err } // log & print response - if r.hrpRunner.requestsLogOn { + if r.LogOn() { if err := printResponse(resp); err != nil { return stepResult, err } } // new response object - respObj, err := newResponseObject(r.hrpRunner.t, r.parser, resp) + respObj, err := newResponseObject(r.hrpRunner.t, parser, resp) if err != nil { err = errors.Wrap(err, "init ResponseObject error") return } // add response object to step variables, could be used in teardown hooks - step.Variables["hrp_step_response"] = respObj.respObjMeta + stepVariables["hrp_step_response"] = respObj.respObjMeta // deal with teardown hooks for _, teardownHook := range step.TeardownHooks { - _, err = r.parser.parseData(teardownHook, step.Variables) + _, err = parser.Parse(teardownHook, stepVariables) if err != nil { return stepResult, errors.Wrap(err, "run teardown hooks failed") } @@ -298,13 +317,8 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err extractMapping := respObj.Extract(extractors) stepResult.ExportVars = extractMapping - // update extracted variables - for k, v := range stepResult.ExportVars { - r.sessionVariables[k] = v - } - // override step variables with extracted variables - stepVariables := mergeVariables(step.Variables, extractMapping) + stepVariables = mergeVariables(stepVariables, extractMapping) // validate response err = respObj.Validate(step.Validators, stepVariables) @@ -316,15 +330,6 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err stepResult.ContentSize = resp.ContentLength stepResult.Data = sessionData - // append step result to summary - r.summary.Records = append(r.summary.Records, stepResult) - r.summary.Stat.Total += 1 - if stepResult.Success { - r.summary.Stat.Successes += 1 - } else { - r.summary.Stat.Failures += 1 - } - return stepResult, err } diff --git a/hrp/step_testcase.go b/hrp/step_testcase.go index 1ef36b37..56b485c7 100644 --- a/hrp/step_testcase.go +++ b/hrp/step_testcase.go @@ -44,18 +44,19 @@ func (s *StepTestCaseWithOptionalArgs) ToStruct() *TStep { } func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error) { - copiedStep, err := r.overrideVariables(s.step) + stepVariables, err := r.MergeStepVariables(s.step.Variables) if err != nil { return nil, err } + s.step.Variables = stepVariables - log.Info().Str("testcase", copiedStep.Name).Msg("run referenced testcase") + log.Info().Str("testcase", s.step.Name).Msg("run referenced testcase") stepResult := &StepResult{ - Name: copiedStep.Name, + Name: s.step.Name, StepType: stepTypeTestCase, Success: false, } - testcase := copiedStep.TestCase.(*TestCase) + testcase := s.step.TestCase.(*TestCase) // copy testcase to avoid data racing copiedTestCase := &TestCase{} @@ -64,21 +65,21 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error return stepResult, err } // override testcase config - extendWithTestCase(copiedStep, copiedTestCase) + extendWithTestCase(s.step, copiedTestCase) sessionRunner := r.hrpRunner.NewSessionRunner(copiedTestCase) start := time.Now() - err = sessionRunner.Run() + err = sessionRunner.Start() stepResult.Elapsed = time.Since(start).Milliseconds() if err != nil { log.Error().Err(err).Msg("run referenced testcase step failed") - log.Info().Str("step", copiedStep.Name).Bool("success", false).Msg("run step end") + log.Info().Str("step", s.step.Name).Bool("success", false).Msg("run step end") stepResult.Attachment = err.Error() r.summary.Success = false return stepResult, err } - summary := sessionRunner.getSummary() + summary := sessionRunner.GetSummary() stepResult.Data = summary // export testcase export variables stepResult.ExportVars = sessionRunner.summary.InOut.ExportVars @@ -96,10 +97,22 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error r.summary.Stat.Failures += summary.Stat.Failures log.Info(). - Str("step", copiedStep.Name). + Str("step", s.step.Name). Bool("success", true). Interface("exportVars", stepResult.ExportVars). Msg("run step end") return stepResult, nil } + +// extend referenced testcase with teststep, teststep config merge and override referenced testcase config +func extendWithTestCase(testStep *TStep, overriddenTestCase *TestCase) { + // override testcase name + if testStep.Name != "" { + overriddenTestCase.Config.Name = testStep.Name + } + // merge & override variables + overriddenTestCase.Config.Variables = mergeVariables(testStep.Variables, overriddenTestCase.Config.Variables) + // merge & override extractors + overriddenTestCase.Config.Export = mergeSlices(testStep.Export, overriddenTestCase.Config.Export) +}