mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-13 17:29:56 +08:00
refactor: TestCaseSessionRunner
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user