mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
feat: data-driven.
This commit is contained in:
@@ -76,8 +76,10 @@ func (b *hrpBoomer) convertBoomerTask(testcase *TestCase) *boomer.Task {
|
|||||||
if err := copier.Copy(caseConfig, cfg); err != nil {
|
if err := copier.Copy(caseConfig, cfg); err != nil {
|
||||||
log.Error().Err(err).Msg("copy config data failed")
|
log.Error().Err(err).Msg("copy config data failed")
|
||||||
}
|
}
|
||||||
if it := cfg.ParametersSetting.Iterator; it.HasNext() {
|
for _, it := range cfg.ParametersSetting.Iterator {
|
||||||
caseConfig.Variables = mergeVariables(it.Next(), caseConfig.Variables)
|
if it.HasNext() {
|
||||||
|
caseConfig.Variables = mergeVariables(it.Next(), caseConfig.Variables)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
for index, step := range testcase.TestSteps {
|
for index, step := range testcase.TestSteps {
|
||||||
|
|||||||
@@ -9,8 +9,11 @@
|
|||||||
"username-password": "${parameterize(examples/account.csv)}"
|
"username-password": "${parameterize(examples/account.csv)}"
|
||||||
},
|
},
|
||||||
"parameters_setting": {
|
"parameters_setting": {
|
||||||
"strategy": "random",
|
"strategy": [
|
||||||
"iteration": 1
|
"random",
|
||||||
|
"sequential"
|
||||||
|
],
|
||||||
|
"iteration": 10
|
||||||
},
|
},
|
||||||
"variables": {
|
"variables": {
|
||||||
"app_version": "v1",
|
"app_version": "v1",
|
||||||
|
|||||||
@@ -1,39 +1,38 @@
|
|||||||
config:
|
config:
|
||||||
name: "request methods testcase: validate with parameters"
|
name: "request methods testcase: validate with parameters"
|
||||||
parameters:
|
parameters:
|
||||||
user_agent: ["iOS/10.1", "iOS/10.2"]
|
user_agent: [ "iOS/10.1", "iOS/10.2" ]
|
||||||
username-password: ${parameterize(examples/account.csv)}
|
username-password: ${parameterize(examples/account.csv)}
|
||||||
parameters_setting:
|
parameters_setting:
|
||||||
strategy: random
|
strategy: ["random", "sequential"]
|
||||||
iteration: 1
|
iteration: 10
|
||||||
variables:
|
variables:
|
||||||
app_version: v1
|
app_version: v1
|
||||||
user_agent: iOS/10.3
|
user_agent: iOS/10.3
|
||||||
base_url: "https://postman-echo.com"
|
base_url: "https://postman-echo.com"
|
||||||
verify: False
|
verify: False
|
||||||
|
|
||||||
teststeps:
|
teststeps:
|
||||||
-
|
- name: get with params
|
||||||
name: get with params
|
|
||||||
variables:
|
variables:
|
||||||
foo1: $username
|
foo1: $username
|
||||||
foo2: $password
|
foo2: $password
|
||||||
foo3: $user_agent
|
foo3: $user_agent
|
||||||
request:
|
request:
|
||||||
method: GET
|
method: GET
|
||||||
url: /get
|
url: /get
|
||||||
params:
|
params:
|
||||||
foo1: $foo1
|
foo1: $foo1
|
||||||
foo2: $foo2
|
foo2: $foo2
|
||||||
foo3: $foo3
|
foo3: $foo3
|
||||||
headers:
|
headers:
|
||||||
User-Agent: $user_agent,$app_version
|
User-Agent: $user_agent,$app_version
|
||||||
validate:
|
validate:
|
||||||
- check: status_code
|
- check: status_code
|
||||||
assert: equals
|
assert: equals
|
||||||
expect: 200
|
expect: 200
|
||||||
msg: check status code
|
msg: check status code
|
||||||
- check: body.args.foo3
|
- check: body.args.foo3
|
||||||
assert: not_equal
|
assert: not_equal
|
||||||
expect: iOS/10.3
|
expect: iOS/10.3
|
||||||
msg: check app version
|
msg: check app version
|
||||||
@@ -30,9 +30,9 @@ type TConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TParamsConfig struct {
|
type TParamsConfig struct {
|
||||||
Strategy string `json:"strategy,omitempty" yaml:"strategy,omitempty"`
|
Strategy interface{} `json:"strategy,omitempty" yaml:"strategy,omitempty"`
|
||||||
Iteration int `json:"iteration,omitempty" yaml:"iteration,omitempty"`
|
Iteration int `json:"iteration,omitempty" yaml:"iteration,omitempty"`
|
||||||
Iterator *Iterator `json:"parameterIterator,omitempty" yaml:"parameterIterator,omitempty"`
|
Iterator []*Iterator `json:"parameterIterator,omitempty" yaml:"parameterIterator,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -68,7 +68,6 @@ func (iter *Iterator) HasNext() bool {
|
|||||||
func (iter *Iterator) Next() (value map[string]interface{}) {
|
func (iter *Iterator) Next() (value map[string]interface{}) {
|
||||||
iter.Lock()
|
iter.Lock()
|
||||||
defer iter.Unlock()
|
defer iter.Unlock()
|
||||||
iter.index++
|
|
||||||
if len(iter.data) == 0 {
|
if len(iter.data) == 0 {
|
||||||
return map[string]interface{}{}
|
return map[string]interface{}{}
|
||||||
}
|
}
|
||||||
@@ -79,6 +78,7 @@ func (iter *Iterator) Next() (value map[string]interface{}) {
|
|||||||
} else {
|
} else {
|
||||||
value = iter.data[iter.index%len(iter.data)]
|
value = iter.data[iter.index%len(iter.data)]
|
||||||
}
|
}
|
||||||
|
iter.index++
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -494,14 +494,14 @@ func findallVariables(raw string) variableSet {
|
|||||||
return varSet
|
return varSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func genCartesianProduct(params [][]map[string]interface{}) []map[string]interface{} {
|
func genCartesianProduct(params []paramsType) paramsType {
|
||||||
if len(params) == 0 {
|
if len(params) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var cartesianProduct []map[string]interface{}
|
var cartesianProduct paramsType
|
||||||
cartesianProduct = params[0]
|
cartesianProduct = params[0]
|
||||||
for i := 0; i < len(params)-1; i++ {
|
for i := 0; i < len(params)-1; i++ {
|
||||||
var tempProduct []map[string]interface{}
|
var tempProduct paramsType
|
||||||
for _, param1 := range cartesianProduct {
|
for _, param1 := range cartesianProduct {
|
||||||
for _, param2 := range params[i+1] {
|
for _, param2 := range params[i+1] {
|
||||||
tempProduct = append(tempProduct, mergeVariables(param1, param2))
|
tempProduct = append(tempProduct, mergeVariables(param1, param2))
|
||||||
@@ -512,14 +512,14 @@ func genCartesianProduct(params [][]map[string]interface{}) []map[string]interfa
|
|||||||
return cartesianProduct
|
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 {
|
if len(parameters) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var parsedParametersSlice [][]map[string]interface{}
|
var parsedParametersSlice []paramsType
|
||||||
var err error
|
var err error
|
||||||
for k, v := range parameters {
|
for k, v := range parameters {
|
||||||
var parameterSlice []map[string]interface{}
|
var parameterSlice paramsType
|
||||||
rawValue := reflect.ValueOf(v)
|
rawValue := reflect.ValueOf(v)
|
||||||
switch rawValue.Kind() {
|
switch rawValue.Kind() {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
@@ -548,7 +548,7 @@ func parseParameters(parameters map[string]interface{}, variablesMapping map[str
|
|||||||
}
|
}
|
||||||
parsedParametersSlice = append(parsedParametersSlice, parameterSlice)
|
parsedParametersSlice = append(parsedParametersSlice, parameterSlice)
|
||||||
}
|
}
|
||||||
return genCartesianProduct(parsedParametersSlice), nil
|
return parsedParametersSlice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSlice(parameterName string, parameterContent interface{}) ([]map[string]interface{}, error) {
|
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) {
|
func initParameterIterator(cfg *TConfig, mode string) (err error) {
|
||||||
var parameters paramsType
|
var parameters []paramsType
|
||||||
parameters, err = parseParameters(cfg.Parameters, cfg.Variables)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(cfg.ParametersSetting.Strategy) == 0 {
|
// parse config parameters setting
|
||||||
cfg.ParametersSetting.Strategy = strategySequential
|
if cfg.ParametersSetting == nil {
|
||||||
} else {
|
cfg.ParametersSetting = &TParamsConfig{Iterator: []*Iterator{}}
|
||||||
cfg.ParametersSetting.Strategy = strings.ToLower(cfg.ParametersSetting.Strategy)
|
|
||||||
}
|
}
|
||||||
cfg.ParametersSetting.Iterator.strategy = cfg.ParametersSetting.Strategy
|
|
||||||
if mode == "boomer" {
|
if mode == "boomer" {
|
||||||
cfg.ParametersSetting.Iteration = -1
|
cfg.ParametersSetting.Iteration = -1
|
||||||
cfg.ParametersSetting.Iterator.iteration = cfg.ParametersSetting.Iteration
|
}
|
||||||
} else {
|
rawValue := reflect.ValueOf(cfg.ParametersSetting.Strategy)
|
||||||
if cfg.ParametersSetting.Iteration > 0 {
|
switch rawValue.Kind() {
|
||||||
cfg.ParametersSetting.Iterator.iteration = cfg.ParametersSetting.Iteration
|
case reflect.String:
|
||||||
} else if cfg.ParametersSetting.Iterator.iteration == 0 {
|
if len(rawValue.String()) == 0 {
|
||||||
cfg.ParametersSetting.Iterator.iteration = 1
|
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
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -143,8 +143,12 @@ func (r *caseRunner) run() error {
|
|||||||
}
|
}
|
||||||
cfg := config.ToStruct()
|
cfg := config.ToStruct()
|
||||||
log.Info().Str("testcase", config.Name()).Msg("run testcase start")
|
log.Info().Str("testcase", config.Name()).Msg("run testcase start")
|
||||||
for it := cfg.ParametersSetting.Iterator; it.HasNext(); {
|
for it := cfg.ParametersSetting.Iterator[0]; it.HasNext(); {
|
||||||
cfg.Variables = mergeVariables(it.Next(), cfg.Variables)
|
for _, it = range cfg.ParametersSetting.Iterator {
|
||||||
|
if it.HasNext() {
|
||||||
|
cfg.Variables = mergeVariables(it.Next(), cfg.Variables)
|
||||||
|
}
|
||||||
|
}
|
||||||
r.startTime = time.Now()
|
r.startTime = time.Now()
|
||||||
for index := range r.TestCase.TestSteps {
|
for index := range r.TestCase.TestSteps {
|
||||||
_, err := r.runStep(index, cfg)
|
_, err := r.runStep(index, cfg)
|
||||||
|
|||||||
Reference in New Issue
Block a user