refactor: TestCaseSessionRunner

This commit is contained in:
debugtalk
2022-03-30 12:06:16 +08:00
parent b3610e550b
commit 20d0749841
9 changed files with 173 additions and 158 deletions

View File

@@ -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
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)
}