mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-13 00:11:28 +08:00
feat: set testcase and request timeout in seconds
This commit is contained in:
@@ -37,6 +37,7 @@ var (
|
|||||||
proxyUrl string
|
proxyUrl string
|
||||||
saveTests bool
|
saveTests bool
|
||||||
genHTMLReport bool
|
genHTMLReport bool
|
||||||
|
caseTimeout float32
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -48,12 +49,14 @@ func init() {
|
|||||||
runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url")
|
runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url")
|
||||||
runCmd.Flags().BoolVarP(&saveTests, "save-tests", "s", false, "save tests summary")
|
runCmd.Flags().BoolVarP(&saveTests, "save-tests", "s", false, "save tests summary")
|
||||||
runCmd.Flags().BoolVarP(&genHTMLReport, "gen-html-report", "g", false, "generate html report")
|
runCmd.Flags().BoolVarP(&genHTMLReport, "gen-html-report", "g", false, "generate html report")
|
||||||
|
runCmd.Flags().Float32Var(&caseTimeout, "case-timeout", 3600, "set testcase timeout (seconds)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeHRPRunner() *hrp.HRPRunner {
|
func makeHRPRunner() *hrp.HRPRunner {
|
||||||
runner := hrp.NewRunner(nil).
|
runner := hrp.NewRunner(nil).
|
||||||
SetFailfast(!continueOnFailure).
|
SetFailfast(!continueOnFailure).
|
||||||
SetSaveTests(saveTests)
|
SetSaveTests(saveTests).
|
||||||
|
SetCaseTimeout(caseTimeout)
|
||||||
if genHTMLReport {
|
if genHTMLReport {
|
||||||
runner.GenHTMLReport()
|
runner.GenHTMLReport()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package hrp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||||
@@ -32,7 +31,8 @@ type TConfig struct {
|
|||||||
WebSocketSetting *WebSocketConfig `json:"websocket,omitempty" yaml:"websocket,omitempty"`
|
WebSocketSetting *WebSocketConfig `json:"websocket,omitempty" yaml:"websocket,omitempty"`
|
||||||
IOS []*uixt.IOSDevice `json:"ios,omitempty" yaml:"ios,omitempty"`
|
IOS []*uixt.IOSDevice `json:"ios,omitempty" yaml:"ios,omitempty"`
|
||||||
Android []*uixt.AndroidDevice `json:"android,omitempty" yaml:"android,omitempty"`
|
Android []*uixt.AndroidDevice `json:"android,omitempty" yaml:"android,omitempty"`
|
||||||
Timeout float64 `json:"timeout,omitempty" yaml:"timeout,omitempty"` // global timeout in seconds
|
RequestTimeout float32 `json:"request_timeout,omitempty" yaml:"request_timeout,omitempty"` // request timeout in seconds
|
||||||
|
CaseTimeout float32 `json:"case_timeout,omitempty" yaml:"case_timeout,omitempty"` // testcase timeout in seconds
|
||||||
Export []string `json:"export,omitempty" yaml:"export,omitempty"`
|
Export []string `json:"export,omitempty" yaml:"export,omitempty"`
|
||||||
Weight int `json:"weight,omitempty" yaml:"weight,omitempty"`
|
Weight int `json:"weight,omitempty" yaml:"weight,omitempty"`
|
||||||
Path string `json:"path,omitempty" yaml:"path,omitempty"` // testcase file path
|
Path string `json:"path,omitempty" yaml:"path,omitempty"` // testcase file path
|
||||||
@@ -75,9 +75,15 @@ func (c *TConfig) SetThinkTime(strategy thinkTimeStrategy, cfg interface{}, limi
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTimeout sets testcase timeout in seconds.
|
// SetRequestTimeout sets request timeout in seconds.
|
||||||
func (c *TConfig) SetTimeout(timeout time.Duration) *TConfig {
|
func (c *TConfig) SetRequestTimeout(seconds float32) *TConfig {
|
||||||
c.Timeout = timeout.Seconds()
|
c.RequestTimeout = seconds
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCaseTimeout sets testcase timeout in seconds.
|
||||||
|
func (c *TConfig) SetCaseTimeout(seconds float32) *TConfig {
|
||||||
|
c.CaseTimeout = seconds
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
158
hrp/runner.go
158
hrp/runner.go
@@ -21,6 +21,7 @@ import (
|
|||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||||
|
"github.com/httprunner/httprunner/v4/hrp/internal/code"
|
||||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||||
"github.com/httprunner/httprunner/v4/hrp/internal/version"
|
"github.com/httprunner/httprunner/v4/hrp/internal/version"
|
||||||
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||||
@@ -60,22 +61,24 @@ func NewRunner(t *testing.T) *HRPRunner {
|
|||||||
wsDialer: &websocket.Dialer{
|
wsDialer: &websocket.Dialer{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
},
|
},
|
||||||
|
caseTimeoutTimer: time.NewTimer(time.Hour * 2), // default case timeout to 2 hour
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type HRPRunner struct {
|
type HRPRunner struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
failfast bool
|
failfast bool
|
||||||
httpStatOn bool
|
httpStatOn bool
|
||||||
requestsLogOn bool
|
requestsLogOn bool
|
||||||
pluginLogOn bool
|
pluginLogOn bool
|
||||||
venv string
|
venv string
|
||||||
saveTests bool
|
saveTests bool
|
||||||
genHTMLReport bool
|
genHTMLReport bool
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
http2Client *http.Client
|
http2Client *http.Client
|
||||||
wsDialer *websocket.Dialer
|
wsDialer *websocket.Dialer
|
||||||
uiClients map[string]*uixt.DriverExt // UI automation clients for iOS and Android, key is udid/serial
|
uiClients map[string]*uixt.DriverExt // UI automation clients for iOS and Android, key is udid/serial
|
||||||
|
caseTimeoutTimer *time.Timer // case timeout timer
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClientTransport configures transport of http client for high concurrency load testing
|
// SetClientTransport configures transport of http client for high concurrency load testing
|
||||||
@@ -152,10 +155,17 @@ func (r *HRPRunner) SetProxyUrl(proxyUrl string) *HRPRunner {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTimeout configures global timeout in seconds.
|
// SetRequestTimeout configures global request timeout in seconds.
|
||||||
func (r *HRPRunner) SetTimeout(timeout time.Duration) *HRPRunner {
|
func (r *HRPRunner) SetRequestTimeout(seconds float32) *HRPRunner {
|
||||||
log.Info().Float64("timeout(seconds)", timeout.Seconds()).Msg("[init] SetTimeout")
|
log.Info().Float32("timeout_seconds", seconds).Msg("[init] SetRequestTimeout")
|
||||||
r.httpClient.Timeout = timeout
|
r.httpClient.Timeout = time.Duration(seconds*1000) * time.Millisecond
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCaseTimeout configures global testcase timeout in seconds.
|
||||||
|
func (r *HRPRunner) SetCaseTimeout(seconds float32) *HRPRunner {
|
||||||
|
log.Info().Float32("timeout_seconds", seconds).Msg("[init] SetCaseTimeout")
|
||||||
|
r.caseTimeoutTimer = time.NewTimer(time.Duration(seconds*1000) * time.Millisecond)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,10 +301,13 @@ func (r *HRPRunner) NewCaseRunner(testcase *TestCase) (*CaseRunner, error) {
|
|||||||
return nil, errors.Wrap(err, "parse testcase config failed")
|
return nil, errors.Wrap(err, "parse testcase config failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set request timeout in seconds
|
||||||
|
if testcase.Config.RequestTimeout != 0 {
|
||||||
|
r.SetRequestTimeout(testcase.Config.RequestTimeout)
|
||||||
|
}
|
||||||
// set testcase timeout in seconds
|
// set testcase timeout in seconds
|
||||||
if testcase.Config.Timeout != 0 {
|
if testcase.Config.CaseTimeout != 0 {
|
||||||
timeout := time.Duration(testcase.Config.Timeout*1000) * time.Millisecond
|
r.SetCaseTimeout(testcase.Config.CaseTimeout)
|
||||||
r.SetTimeout(timeout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load plugin info to testcase config
|
// load plugin info to testcase config
|
||||||
@@ -505,66 +518,71 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error {
|
|||||||
|
|
||||||
// run step in sequential order
|
// run step in sequential order
|
||||||
for _, step := range r.caseRunner.testCase.TestSteps {
|
for _, step := range r.caseRunner.testCase.TestSteps {
|
||||||
// TODO: parse step struct
|
select {
|
||||||
// parse step name
|
case <-r.caseRunner.hrpRunner.caseTimeoutTimer.C:
|
||||||
parsedName, err := r.caseRunner.parser.ParseString(step.Name(), r.sessionVariables)
|
return errors.Wrap(code.TimeoutError, "session runner timeout")
|
||||||
if err != nil {
|
default:
|
||||||
parsedName = step.Name()
|
// TODO: parse step struct
|
||||||
}
|
// parse step name
|
||||||
stepName := convertString(parsedName)
|
parsedName, err := r.caseRunner.parser.ParseString(step.Name(), r.sessionVariables)
|
||||||
log.Info().Str("step", stepName).
|
if err != nil {
|
||||||
Str("type", string(step.Type())).Msg("run step start")
|
parsedName = step.Name()
|
||||||
|
}
|
||||||
|
stepName := convertString(parsedName)
|
||||||
|
log.Info().Str("step", stepName).
|
||||||
|
Str("type", string(step.Type())).Msg("run step start")
|
||||||
|
|
||||||
// run times of step
|
// run times of step
|
||||||
loopTimes := step.Struct().Loops
|
loopTimes := step.Struct().Loops
|
||||||
if loopTimes < 0 {
|
if loopTimes < 0 {
|
||||||
log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1")
|
log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1")
|
||||||
loopTimes = 1
|
loopTimes = 1
|
||||||
} else if loopTimes == 0 {
|
} else if loopTimes == 0 {
|
||||||
loopTimes = 1
|
loopTimes = 1
|
||||||
} else if loopTimes > 1 {
|
} else if loopTimes > 1 {
|
||||||
log.Info().Int("loops", loopTimes).Msg("run step with specified loop times")
|
log.Info().Int("loops", loopTimes).Msg("run step with specified loop times")
|
||||||
}
|
|
||||||
|
|
||||||
// run step with specified loop times
|
|
||||||
var stepResult *StepResult
|
|
||||||
for i := 1; i <= loopTimes; i++ {
|
|
||||||
var loopIndex string
|
|
||||||
if loopTimes > 1 {
|
|
||||||
log.Info().Int("index", i).Msg("start running step in loop")
|
|
||||||
loopIndex = fmt.Sprintf("_loop_%d", i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// run step
|
// run step with specified loop times
|
||||||
stepResult, err = step.Run(r)
|
var stepResult *StepResult
|
||||||
stepResult.Name = stepName + loopIndex
|
for i := 1; i <= loopTimes; i++ {
|
||||||
|
var loopIndex string
|
||||||
|
if loopTimes > 1 {
|
||||||
|
log.Info().Int("index", i).Msg("start running step in loop")
|
||||||
|
loopIndex = fmt.Sprintf("_loop_%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
r.updateSummary(stepResult)
|
// run step
|
||||||
}
|
stepResult, err = step.Run(r)
|
||||||
|
stepResult.Name = stepName + loopIndex
|
||||||
|
|
||||||
// update extracted variables
|
r.updateSummary(stepResult)
|
||||||
for k, v := range stepResult.ExportVars {
|
}
|
||||||
r.sessionVariables[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
// update extracted variables
|
||||||
log.Info().Str("step", stepResult.Name).
|
for k, v := range stepResult.ExportVars {
|
||||||
|
r.sessionVariables[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.Info().Str("step", stepResult.Name).
|
||||||
|
Str("type", string(stepResult.StepType)).
|
||||||
|
Bool("success", true).
|
||||||
|
Interface("exportVars", stepResult.ExportVars).
|
||||||
|
Msg("run step end")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// failed
|
||||||
|
log.Error().Err(err).Str("step", stepResult.Name).
|
||||||
Str("type", string(stepResult.StepType)).
|
Str("type", string(stepResult.StepType)).
|
||||||
Bool("success", true).
|
Bool("success", false).
|
||||||
Interface("exportVars", stepResult.ExportVars).
|
|
||||||
Msg("run step end")
|
Msg("run step end")
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// failed
|
// check if failfast
|
||||||
log.Error().Err(err).Str("step", stepResult.Name).
|
if r.caseRunner.hrpRunner.failfast {
|
||||||
Str("type", string(stepResult.StepType)).
|
return errors.Wrap(err, "abort running due to failfast setting")
|
||||||
Bool("success", false).
|
}
|
||||||
Msg("run step end")
|
|
||||||
|
|
||||||
// check if failfast
|
|
||||||
if r.caseRunner.hrpRunner.failfast {
|
|
||||||
return errors.Wrap(err, "abort running due to failfast setting")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ func TestRunCaseWithTimeout(t *testing.T) {
|
|||||||
// global timeout
|
// global timeout
|
||||||
testcase1 := &TestCase{
|
testcase1 := &TestCase{
|
||||||
Config: NewConfig("TestCase1").
|
Config: NewConfig("TestCase1").
|
||||||
SetTimeout(10 * time.Second). // set global timeout to 10s
|
SetRequestTimeout(10). // set global timeout to 10s
|
||||||
SetBaseURL("https://httpbin.org"),
|
SetBaseURL("https://httpbin.org"),
|
||||||
TestSteps: []IStep{
|
TestSteps: []IStep{
|
||||||
NewStep("step1").
|
NewStep("step1").
|
||||||
@@ -180,7 +180,7 @@ func TestRunCaseWithTimeout(t *testing.T) {
|
|||||||
|
|
||||||
testcase2 := &TestCase{
|
testcase2 := &TestCase{
|
||||||
Config: NewConfig("TestCase2").
|
Config: NewConfig("TestCase2").
|
||||||
SetTimeout(10 * time.Second). // set global timeout to 10s
|
SetRequestTimeout(10). // set global timeout to 10s
|
||||||
SetBaseURL("https://httpbin.org"),
|
SetBaseURL("https://httpbin.org"),
|
||||||
TestSteps: []IStep{
|
TestSteps: []IStep{
|
||||||
NewStep("step1").
|
NewStep("step1").
|
||||||
@@ -197,7 +197,7 @@ func TestRunCaseWithTimeout(t *testing.T) {
|
|||||||
// step timeout
|
// step timeout
|
||||||
testcase3 := &TestCase{
|
testcase3 := &TestCase{
|
||||||
Config: NewConfig("TestCase3").
|
Config: NewConfig("TestCase3").
|
||||||
SetTimeout(10 * time.Second).
|
SetRequestTimeout(10).
|
||||||
SetBaseURL("https://httpbin.org"),
|
SetBaseURL("https://httpbin.org"),
|
||||||
TestSteps: []IStep{
|
TestSteps: []IStep{
|
||||||
NewStep("step2").
|
NewStep("step2").
|
||||||
|
|||||||
Reference in New Issue
Block a user