diff --git a/boomer.go b/boomer.go index d196cc75..eae914ea 100644 --- a/boomer.go +++ b/boomer.go @@ -76,8 +76,10 @@ func (b *hrpBoomer) convertBoomerTask(testcase *TestCase) *boomer.Task { if err := copier.Copy(caseConfig, cfg); err != nil { log.Error().Err(err).Msg("copy config data failed") } - if it := cfg.ParametersSetting.Iterator; it.HasNext() { - caseConfig.Variables = mergeVariables(it.Next(), caseConfig.Variables) + for _, it := range cfg.ParametersSetting.Iterator { + if it.HasNext() { + caseConfig.Variables = mergeVariables(it.Next(), caseConfig.Variables) + } } startTime := time.Now() for index, step := range testcase.TestSteps { diff --git a/examples/parameters_test.json b/examples/parameters_test.json index e88cf8fb..61bc5ead 100644 --- a/examples/parameters_test.json +++ b/examples/parameters_test.json @@ -9,8 +9,11 @@ "username-password": "${parameterize(examples/account.csv)}" }, "parameters_setting": { - "strategy": "random", - "iteration": 1 + "strategy": [ + "random", + "sequential" + ], + "iteration": 10 }, "variables": { "app_version": "v1", diff --git a/examples/parameters_test.yaml b/examples/parameters_test.yaml index 9ef16dd1..412acaab 100644 --- a/examples/parameters_test.yaml +++ b/examples/parameters_test.yaml @@ -1,39 +1,38 @@ config: - name: "request methods testcase: validate with parameters" - parameters: - user_agent: ["iOS/10.1", "iOS/10.2"] - username-password: ${parameterize(examples/account.csv)} - parameters_setting: - strategy: random - iteration: 1 - variables: - app_version: v1 - user_agent: iOS/10.3 - base_url: "https://postman-echo.com" - verify: False + name: "request methods testcase: validate with parameters" + parameters: + user_agent: [ "iOS/10.1", "iOS/10.2" ] + username-password: ${parameterize(examples/account.csv)} + parameters_setting: + strategy: ["random", "sequential"] + iteration: 10 + variables: + app_version: v1 + user_agent: iOS/10.3 + base_url: "https://postman-echo.com" + verify: False teststeps: -- - name: get with params + - name: get with params variables: - foo1: $username - foo2: $password - foo3: $user_agent + foo1: $username + foo2: $password + foo3: $user_agent request: - method: GET - url: /get - params: - foo1: $foo1 - foo2: $foo2 - foo3: $foo3 - headers: - User-Agent: $user_agent,$app_version + method: GET + url: /get + params: + foo1: $foo1 + foo2: $foo2 + foo3: $foo3 + headers: + User-Agent: $user_agent,$app_version validate: - - check: status_code - assert: equals - expect: 200 - msg: check status code - - check: body.args.foo3 - assert: not_equal - expect: iOS/10.3 - msg: check app version \ No newline at end of file + - check: status_code + assert: equals + expect: 200 + msg: check status code + - check: body.args.foo3 + assert: not_equal + expect: iOS/10.3 + msg: check app version \ No newline at end of file diff --git a/models.go b/models.go index 880d6756..839234e8 100644 --- a/models.go +++ b/models.go @@ -30,9 +30,9 @@ type TConfig struct { } type TParamsConfig struct { - Strategy string `json:"strategy,omitempty" yaml:"strategy,omitempty"` - Iteration int `json:"iteration,omitempty" yaml:"iteration,omitempty"` - Iterator *Iterator `json:"parameterIterator,omitempty" yaml:"parameterIterator,omitempty"` + Strategy interface{} `json:"strategy,omitempty" yaml:"strategy,omitempty"` + Iteration int `json:"iteration,omitempty" yaml:"iteration,omitempty"` + Iterator []*Iterator `json:"parameterIterator,omitempty" yaml:"parameterIterator,omitempty"` } const ( @@ -68,7 +68,6 @@ func (iter *Iterator) HasNext() bool { func (iter *Iterator) Next() (value map[string]interface{}) { iter.Lock() defer iter.Unlock() - iter.index++ if len(iter.data) == 0 { return map[string]interface{}{} } @@ -79,6 +78,7 @@ func (iter *Iterator) Next() (value map[string]interface{}) { } else { value = iter.data[iter.index%len(iter.data)] } + iter.index++ return value } diff --git a/parser.go b/parser.go index 9cd49a00..0a337f37 100644 --- a/parser.go +++ b/parser.go @@ -494,14 +494,14 @@ func findallVariables(raw string) variableSet { return varSet } -func genCartesianProduct(params [][]map[string]interface{}) []map[string]interface{} { +func genCartesianProduct(params []paramsType) paramsType { if len(params) == 0 { return nil } - var cartesianProduct []map[string]interface{} + var cartesianProduct paramsType cartesianProduct = params[0] for i := 0; i < len(params)-1; i++ { - var tempProduct []map[string]interface{} + var tempProduct paramsType for _, param1 := range cartesianProduct { for _, param2 := range params[i+1] { tempProduct = append(tempProduct, mergeVariables(param1, param2)) @@ -512,14 +512,14 @@ func genCartesianProduct(params [][]map[string]interface{}) []map[string]interfa return cartesianProduct } -func parseParameters(parameters map[string]interface{}, variablesMapping map[string]interface{}) ([]map[string]interface{}, error) { +func parseParameters(parameters map[string]interface{}, variablesMapping map[string]interface{}) ([]paramsType, error) { if len(parameters) == 0 { return nil, nil } - var parsedParametersSlice [][]map[string]interface{} + var parsedParametersSlice []paramsType var err error for k, v := range parameters { - var parameterSlice []map[string]interface{} + var parameterSlice paramsType rawValue := reflect.ValueOf(v) switch rawValue.Kind() { case reflect.String: @@ -548,7 +548,7 @@ func parseParameters(parameters map[string]interface{}, variablesMapping map[str } parsedParametersSlice = append(parsedParametersSlice, parameterSlice) } - return genCartesianProduct(parsedParametersSlice), nil + return parsedParametersSlice, nil } func parseSlice(parameterName string, parameterContent interface{}) ([]map[string]interface{}, error) { @@ -599,31 +599,54 @@ func parseSlice(parameterName string, parameterContent interface{}) ([]map[strin } func initParameterIterator(cfg *TConfig, mode string) (err error) { - var parameters paramsType + var parameters []paramsType parameters, err = parseParameters(cfg.Parameters, cfg.Variables) - // parse config parameters setting - if cfg.ParametersSetting == nil { - cfg.ParametersSetting = &TParamsConfig{Iterator: &Iterator{}} - } - cfg.ParametersSetting.Iterator = parameters.Iterator() if err != nil { return err } - if len(cfg.ParametersSetting.Strategy) == 0 { - cfg.ParametersSetting.Strategy = strategySequential - } else { - cfg.ParametersSetting.Strategy = strings.ToLower(cfg.ParametersSetting.Strategy) + // parse config parameters setting + if cfg.ParametersSetting == nil { + cfg.ParametersSetting = &TParamsConfig{Iterator: []*Iterator{}} } - cfg.ParametersSetting.Iterator.strategy = cfg.ParametersSetting.Strategy if mode == "boomer" { cfg.ParametersSetting.Iteration = -1 - cfg.ParametersSetting.Iterator.iteration = cfg.ParametersSetting.Iteration - } else { - if cfg.ParametersSetting.Iteration > 0 { - cfg.ParametersSetting.Iterator.iteration = cfg.ParametersSetting.Iteration - } else if cfg.ParametersSetting.Iterator.iteration == 0 { - cfg.ParametersSetting.Iterator.iteration = 1 + } + rawValue := reflect.ValueOf(cfg.ParametersSetting.Strategy) + switch rawValue.Kind() { + case reflect.String: + if len(rawValue.String()) == 0 { + cfg.ParametersSetting.Strategy = strategySequential + } else { + cfg.ParametersSetting.Strategy = strings.ToLower(rawValue.String()) + } + cfg.ParametersSetting.Iterator = append( + cfg.ParametersSetting.Iterator, + newIterator(genCartesianProduct(parameters), cfg.ParametersSetting.Strategy.(string), cfg.ParametersSetting.Iteration), + ) + case reflect.Slice: + if len(parameters) != rawValue.Len() { + return errors.New("parameters and strategy should have the same length") + } else { + for i := 0; i < rawValue.Len(); i++ { + cfg.ParametersSetting.Iterator = append( + cfg.ParametersSetting.Iterator, + newIterator(parameters[i], rawValue.Index(i).Interface().(string), cfg.ParametersSetting.Iteration), + ) + } } } return nil } + +func newIterator(parameters paramsType, strategy string, iteration int) *Iterator { + it := parameters.Iterator() + it.strategy = strategy + if iteration > 0 { + it.iteration = iteration + } else if it.iteration == 0 { + it.iteration = 1 + } else { + it.iteration = -1 + } + return it +} diff --git a/runner.go b/runner.go index df33b966..d8f7543d 100644 --- a/runner.go +++ b/runner.go @@ -143,8 +143,12 @@ func (r *caseRunner) run() error { } cfg := config.ToStruct() log.Info().Str("testcase", config.Name()).Msg("run testcase start") - for it := cfg.ParametersSetting.Iterator; it.HasNext(); { - cfg.Variables = mergeVariables(it.Next(), cfg.Variables) + for it := cfg.ParametersSetting.Iterator[0]; it.HasNext(); { + for _, it = range cfg.ParametersSetting.Iterator { + if it.HasNext() { + cfg.Variables = mergeVariables(it.Next(), cfg.Variables) + } + } r.startTime = time.Now() for index := range r.TestCase.TestSteps { _, err := r.runStep(index, cfg)