mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-10 17:43:00 +08:00
feat: set step loops with expression variable
This commit is contained in:
@@ -1 +1 @@
|
||||
v5.0.0-beta-2505141436
|
||||
v5.0.0-beta-2505141501
|
||||
|
||||
48
runner.go
48
runner.go
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
@@ -707,14 +708,9 @@ func (r *SessionRunner) RunStep(step IStep) (stepResult *StepResult, err error)
|
||||
log.Info().Str("step", stepName).Str("type", stepType).Msg("run step start")
|
||||
|
||||
// run times of step
|
||||
loopTimes := step.Config().Loops
|
||||
if loopTimes < 0 {
|
||||
log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1")
|
||||
loopTimes = 1
|
||||
} else if loopTimes == 0 {
|
||||
loopTimes = 1
|
||||
} else if loopTimes > 1 {
|
||||
log.Info().Int("loops", loopTimes).Msg("run step with specified loop times")
|
||||
loopTimes, err := r.getLoopTimes(step)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get loop times")
|
||||
}
|
||||
|
||||
// run step with specified loop times
|
||||
@@ -840,3 +836,39 @@ func (r *SessionRunner) GetSessionVariables() map[string]interface{} {
|
||||
func (r *SessionRunner) GetTransactions() map[string]map[TransactionType]time.Time {
|
||||
return r.transactions
|
||||
}
|
||||
|
||||
func (r *SessionRunner) getLoopTimes(step IStep) (int, error) {
|
||||
loops := step.Config().Loops
|
||||
if loops == nil {
|
||||
// default run once
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
loopTimes, err := loops.Value()
|
||||
if err != nil {
|
||||
parsed, err := r.caseRunner.parser.ParseString(
|
||||
*loops.StringValue, step.Config().Variables)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "failed to parse loop times")
|
||||
}
|
||||
switch v := parsed.(type) {
|
||||
case int:
|
||||
loopTimes = v
|
||||
case string:
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "failed to parse loop times")
|
||||
}
|
||||
loopTimes = n
|
||||
}
|
||||
}
|
||||
if loopTimes < 0 {
|
||||
return 0, fmt.Errorf("loop times should be positive, got %d", loopTimes)
|
||||
} else if loopTimes == 0 {
|
||||
loopTimes = 1
|
||||
} else if loopTimes > 1 {
|
||||
log.Info().Int("loops", loopTimes).Msg("set multiple loop times")
|
||||
}
|
||||
|
||||
return loopTimes, nil
|
||||
}
|
||||
|
||||
7
step.go
7
step.go
@@ -1,6 +1,9 @@
|
||||
package hrp
|
||||
|
||||
import "github.com/httprunner/httprunner/v5/uixt"
|
||||
import (
|
||||
"github.com/httprunner/httprunner/v5/uixt"
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
)
|
||||
|
||||
type StepType string
|
||||
|
||||
@@ -31,7 +34,7 @@ type StepConfig struct {
|
||||
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
|
||||
Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
|
||||
StepExport []string `json:"export,omitempty" yaml:"export,omitempty"`
|
||||
Loops int `json:"loops,omitempty" yaml:"loops,omitempty"`
|
||||
Loops *types.IntOrString `json:"loops,omitempty" yaml:"loops,omitempty"`
|
||||
IgnorePopup bool `json:"ignore_popup,omitempty" yaml:"ignore_popup,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/httprunner/httprunner/v5/internal/httpstat"
|
||||
"github.com/httprunner/httprunner/v5/internal/json"
|
||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||
)
|
||||
|
||||
type HTTPMethod string
|
||||
@@ -559,7 +560,9 @@ func (s *StepRequest) HTTP2() *StepRequest {
|
||||
|
||||
// Loop specify running times for the current step
|
||||
func (s *StepRequest) Loop(times int) *StepRequest {
|
||||
s.Loops = times
|
||||
s.Loops = &types.IntOrString{
|
||||
IntValue: ×,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
107
uixt/types/field.go
Normal file
107
uixt/types/field.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// IntOrString supports int or string
|
||||
type IntOrString struct {
|
||||
IntValue *int // e.g 513
|
||||
StringValue *string // e.g "513", "$var"
|
||||
}
|
||||
|
||||
// Value returns the int value, converting from string if necessary
|
||||
func (ios *IntOrString) Value() (int, error) {
|
||||
if ios == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if ios.IntValue != nil {
|
||||
return *ios.IntValue, nil
|
||||
}
|
||||
if ios.StringValue != nil {
|
||||
if *ios.StringValue == "" {
|
||||
return 0, nil
|
||||
}
|
||||
n, err := strconv.Atoi(*ios.StringValue)
|
||||
if err != nil {
|
||||
// variable expression, e.g. "$var"
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// IntValue and StringValue are both nil
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements custom JSON unmarshalling for IntOrString
|
||||
func (ios *IntOrString) UnmarshalJSON(data []byte) error {
|
||||
// Try to unmarshal as int
|
||||
var i int
|
||||
if err := json.Unmarshal(data, &i); err == nil {
|
||||
ios.IntValue = &i
|
||||
ios.StringValue = nil
|
||||
return nil
|
||||
}
|
||||
// Try to unmarshal as string
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err == nil {
|
||||
ios.StringValue = &s
|
||||
ios.IntValue = nil
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid IntOrString data: %s", string(data))
|
||||
}
|
||||
|
||||
// FloatOrString supports float64 or string
|
||||
type FloatOrString struct {
|
||||
FloatValue *float64 // e.g 5.13
|
||||
StringValue *string // e.g "5.13", "$var"
|
||||
}
|
||||
|
||||
// Value returns the float value, converting from string if necessary
|
||||
func (ios *FloatOrString) Value() (float64, error) {
|
||||
if ios == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if ios.FloatValue != nil {
|
||||
return *ios.FloatValue, nil
|
||||
}
|
||||
if ios.StringValue != nil {
|
||||
if *ios.StringValue == "" {
|
||||
return 0, nil
|
||||
}
|
||||
n, err := strconv.ParseFloat(*ios.StringValue, 64)
|
||||
if err != nil {
|
||||
// variable expression, e.g. "$var"
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// IntValue and StringValue are both nil
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements custom JSON unmarshalling for IntOrString
|
||||
func (ios *FloatOrString) UnmarshalJSON(data []byte) error {
|
||||
// Try to unmarshal as float
|
||||
var f float64
|
||||
if err := json.Unmarshal(data, &f); err == nil {
|
||||
ios.FloatValue = &f
|
||||
ios.StringValue = nil
|
||||
return nil
|
||||
}
|
||||
// Try to unmarshal as string
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err == nil {
|
||||
ios.StringValue = &s
|
||||
ios.FloatValue = nil
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid FloatOrString data: %s", string(data))
|
||||
}
|
||||
Reference in New Issue
Block a user