mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-06 16:07:45 +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
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"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")
|
log.Info().Str("step", stepName).Str("type", stepType).Msg("run step start")
|
||||||
|
|
||||||
// run times of step
|
// run times of step
|
||||||
loopTimes := step.Config().Loops
|
loopTimes, err := r.getLoopTimes(step)
|
||||||
if loopTimes < 0 {
|
if err != nil {
|
||||||
log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1")
|
return nil, errors.Wrap(err, "failed to get loop times")
|
||||||
loopTimes = 1
|
|
||||||
} else if loopTimes == 0 {
|
|
||||||
loopTimes = 1
|
|
||||||
} else if loopTimes > 1 {
|
|
||||||
log.Info().Int("loops", loopTimes).Msg("run step with specified loop times")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// run step with specified 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 {
|
func (r *SessionRunner) GetTransactions() map[string]map[TransactionType]time.Time {
|
||||||
return r.transactions
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package hrp
|
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
|
type StepType string
|
||||||
|
|
||||||
@@ -31,7 +34,7 @@ type StepConfig struct {
|
|||||||
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
|
Extract map[string]string `json:"extract,omitempty" yaml:"extract,omitempty"`
|
||||||
Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
|
Validators []interface{} `json:"validate,omitempty" yaml:"validate,omitempty"`
|
||||||
StepExport []string `json:"export,omitempty" yaml:"export,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"`
|
IgnorePopup bool `json:"ignore_popup,omitempty" yaml:"ignore_popup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/internal/httpstat"
|
"github.com/httprunner/httprunner/v5/internal/httpstat"
|
||||||
"github.com/httprunner/httprunner/v5/internal/json"
|
"github.com/httprunner/httprunner/v5/internal/json"
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
|
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPMethod string
|
type HTTPMethod string
|
||||||
@@ -559,7 +560,9 @@ func (s *StepRequest) HTTP2() *StepRequest {
|
|||||||
|
|
||||||
// Loop specify running times for the current step
|
// Loop specify running times for the current step
|
||||||
func (s *StepRequest) Loop(times int) *StepRequest {
|
func (s *StepRequest) Loop(times int) *StepRequest {
|
||||||
s.Loops = times
|
s.Loops = &types.IntOrString{
|
||||||
|
IntValue: ×,
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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