From 0a9332abf3971a17a84afec5d98ec8e9295eca29 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sun, 26 Jun 2022 11:21:25 +0800 Subject: [PATCH] feat: support setting global testcase timeout and step timeout --- docs/CHANGELOG.md | 3 ++- hrp/config.go | 8 ++++++ hrp/runner.go | 13 ++++++++++ hrp/step_request.go | 31 +++++++++++++++------- hrp/step_request_test.go | 56 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7196c7ba..1086a3b7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,9 +1,10 @@ # Release History -## v4.1.5 (2022-06-22) +## v4.1.5 (2022-06-26) **go version** +- feat: support setting global testcase timeout and step timeout - change: set http request timeout default to 120s - fix: insert response cookies into request for redirect requests diff --git a/hrp/config.go b/hrp/config.go index 404e51c9..8d50c690 100644 --- a/hrp/config.go +++ b/hrp/config.go @@ -2,6 +2,7 @@ package hrp import ( "reflect" + "time" "github.com/httprunner/httprunner/v4/hrp/internal/builtin" ) @@ -28,6 +29,7 @@ type TConfig struct { ParametersSetting *TParamsConfig `json:"parameters_setting,omitempty" yaml:"parameters_setting,omitempty"` ThinkTimeSetting *ThinkTimeConfig `json:"think_time,omitempty" yaml:"think_time,omitempty"` WebSocketSetting *WebSocketConfig `json:"websocket,omitempty" yaml:"websocket,omitempty"` + Timeout float64 `json:"timeout,omitempty" yaml:"timeout,omitempty"` // global timeout in seconds Export []string `json:"export,omitempty" yaml:"export,omitempty"` Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` Path string `json:"path,omitempty" yaml:"path,omitempty"` // testcase file path @@ -69,6 +71,12 @@ func (c *TConfig) SetThinkTime(strategy thinkTimeStrategy, cfg interface{}, limi return c } +// SetTimeout sets testcase timeout in seconds. +func (c *TConfig) SetTimeout(timeout time.Duration) *TConfig { + c.Timeout = timeout.Seconds() + return c +} + // ExportVars specifies variable names to export for current testcase. func (c *TConfig) ExportVars(vars ...string) *TConfig { c.Export = vars diff --git a/hrp/runner.go b/hrp/runner.go index b7b24b17..3442b978 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -143,6 +143,13 @@ func (r *HRPRunner) SetProxyUrl(proxyUrl string) *HRPRunner { return r } +// SetTimeout configures global timeout in seconds. +func (r *HRPRunner) SetTimeout(timeout time.Duration) *HRPRunner { + log.Info().Float64("timeout(seconds)", timeout.Seconds()).Msg("[init] SetTimeout") + r.httpClient.Timeout = timeout + return r +} + // SetSaveTests configures whether to save summary of tests. func (r *HRPRunner) SetSaveTests(saveTests bool) *HRPRunner { log.Info().Bool("saveTests", saveTests).Msg("[init] SetSaveTests") @@ -260,6 +267,12 @@ func (r *HRPRunner) newCaseRunner(testcase *TestCase) (*testCaseRunner, error) { return nil, errors.Wrap(err, "parse testcase config failed") } + // set testcase timeout in seconds + if runner.testCase.Config.Timeout != 0 { + timeout := time.Duration(runner.testCase.Config.Timeout*1000) * time.Millisecond + runner.hrpRunner.SetTimeout(timeout) + } + return runner, nil } diff --git a/hrp/step_request.go b/hrp/step_request.go index 8c4de3a4..8fb423b2 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -49,7 +49,7 @@ type Request struct { Body interface{} `json:"body,omitempty" yaml:"body,omitempty"` Json interface{} `json:"json,omitempty" yaml:"json,omitempty"` Data interface{} `json:"data,omitempty" yaml:"data,omitempty"` - Timeout float32 `json:"timeout,omitempty" yaml:"timeout,omitempty"` + Timeout float64 `json:"timeout,omitempty" yaml:"timeout,omitempty"` // timeout in seconds AllowRedirects bool `json:"allow_redirects,omitempty" yaml:"allow_redirects,omitempty"` Verify bool `json:"verify,omitempty" yaml:"verify,omitempty"` } @@ -325,14 +325,22 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err rb.req = rb.req.WithContext(ctx) } + // select HTTP client + var client *http.Client + if step.Request.HTTP2 { + client = r.hrpRunner.http2Client + } else { + client = r.hrpRunner.httpClient + } + + // set step timeout + if step.Request.Timeout != 0 { + client.Timeout = time.Duration(step.Request.Timeout*1000) * time.Millisecond + } + // do request action start := time.Now() - var resp *http.Response - if step.Request.HTTP2 { - resp, err = r.hrpRunner.http2Client.Do(rb.req) - } else { - resp, err = r.hrpRunner.httpClient.Do(rb.req) - } + resp, err := client.Do(rb.req) if err != nil { return stepResult, errors.Wrap(err, "do request failed") } @@ -726,30 +734,35 @@ type StepRequestWithOptionalArgs struct { // SetVerify sets whether to verify SSL for current HTTP request. func (s *StepRequestWithOptionalArgs) SetVerify(verify bool) *StepRequestWithOptionalArgs { + log.Info().Bool("verify", verify).Msg("set step request verify") s.step.Request.Verify = verify return s } // SetTimeout sets timeout for current HTTP request. -func (s *StepRequestWithOptionalArgs) SetTimeout(timeout float32) *StepRequestWithOptionalArgs { - s.step.Request.Timeout = timeout +func (s *StepRequestWithOptionalArgs) SetTimeout(timeout time.Duration) *StepRequestWithOptionalArgs { + log.Info().Float64("timeout(seconds)", timeout.Seconds()).Msg("set step request timeout") + s.step.Request.Timeout = timeout.Seconds() return s } // SetProxies sets proxies for current HTTP request. func (s *StepRequestWithOptionalArgs) SetProxies(proxies map[string]string) *StepRequestWithOptionalArgs { + log.Info().Interface("proxies", proxies).Msg("set step request proxies") // TODO return s } // SetAllowRedirects sets whether to allow redirects for current HTTP request. func (s *StepRequestWithOptionalArgs) SetAllowRedirects(allowRedirects bool) *StepRequestWithOptionalArgs { + log.Info().Bool("allowRedirects", allowRedirects).Msg("set step request allowRedirects") s.step.Request.AllowRedirects = allowRedirects return s } // SetAuth sets auth for current HTTP request. func (s *StepRequestWithOptionalArgs) SetAuth(auth map[string]string) *StepRequestWithOptionalArgs { + log.Info().Interface("auth", auth).Msg("set step request auth") // TODO return s } diff --git a/hrp/step_request_test.go b/hrp/step_request_test.go index 1076b31d..338d7e7e 100644 --- a/hrp/step_request_test.go +++ b/hrp/step_request_test.go @@ -2,6 +2,7 @@ package hrp import ( "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -172,3 +173,58 @@ func TestRunRequestStatOn(t *testing.T) { t.Fatal() } } + +func TestRunCaseWithTimeout(t *testing.T) { + r := NewRunner(t) + + // global timeout + testcase1 := &TestCase{ + Config: NewConfig("TestCase1"). + SetTimeout(2 * time.Second). // set global timeout to 2s + SetBaseURL("http://httpbin.org"), + TestSteps: []IStep{ + NewStep("step1"). + GET("/delay/1"). + Validate(). + AssertEqual("status_code", 200, "check status code"), + }, + } + err := r.Run(testcase1) + if !assert.NoError(t, err) { // assert no error + t.FailNow() + } + + testcase2 := &TestCase{ + Config: NewConfig("TestCase2"). + SetTimeout(2 * time.Second). // set global timeout to 2s + SetBaseURL("http://httpbin.org"), + TestSteps: []IStep{ + NewStep("step1"). + GET("/delay/3"). + Validate(). + AssertEqual("status_code", 200, "check status code"), + }, + } + err = r.Run(testcase2) + if !assert.Error(t, err) { // assert error + t.FailNow() + } + + // step timeout + testcase3 := &TestCase{ + Config: NewConfig("TestCase3"). + SetTimeout(2 * time.Second). + SetBaseURL("http://httpbin.org"), + TestSteps: []IStep{ + NewStep("step2"). + GET("/delay/3"). + SetTimeout(4*time.Second). // set step timeout to 4s + Validate(). + AssertEqual("status_code", 200, "check status code"), + }, + } + err = r.Run(testcase3) + if !assert.NoError(t, err) { + t.FailNow() + } +}