mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-07 15:31:23 +08:00
change: move step logs to session runner
This commit is contained in:
95
hrp/README.md
Normal file
95
hrp/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
## 核心数据结构
|
||||
|
||||
HttpRunner 以 `TestCase` 为核心,将任意测试场景抽象为有序步骤的集合。
|
||||
|
||||
```go
|
||||
type TestCase struct {
|
||||
Config *TConfig
|
||||
TestSteps []IStep
|
||||
}
|
||||
```
|
||||
|
||||
其中,测试步骤 `IStep` 采用了 `go interface` 的设计理念,支持进行任意拓展;步骤内容统一在 `Run` 方法中进行实现。
|
||||
|
||||
```go
|
||||
type IStep interface {
|
||||
Name() string
|
||||
Type() StepType
|
||||
Struct() *TStep
|
||||
Run(*SessionRunner) (*StepResult, error)
|
||||
}
|
||||
```
|
||||
|
||||
我们只需遵循 `IStep` 的接口定义,即可实现各种类型的测试步骤类型。当前已支持的步骤类型包括:
|
||||
|
||||
- request:发起单次 HTTP 请求
|
||||
- api:引用执行其它 API 文件
|
||||
- testcase:引用执行其它测试用例文件
|
||||
- thinktime:思考时间,按照配置的逻辑进行等待
|
||||
- transaction:事务机制,用于压测
|
||||
- rendezvous:集合点机制,用于压测
|
||||
|
||||
基于该机制,我们可以扩展支持新的协议类型,例如 HTTP2/WebSocket/RPC 等;同时也可以支持新的测试类型,例如 UI 自动化。甚至我们还可以在一个测试用例中混合调用多种不同的 Step 类型,例如实现 HTTP/RPC/UI 混合场景。
|
||||
|
||||
## 运行主流程
|
||||
|
||||
### 整体控制器 HRPRunner
|
||||
|
||||
执行接口测试时,会初始化一个 `HRPRunner`,用于控制测试的执行策略。
|
||||
|
||||
```go
|
||||
type HRPRunner struct {
|
||||
t *testing.T
|
||||
failfast bool
|
||||
requestsLogOn bool
|
||||
pluginLogOn bool
|
||||
saveTests bool
|
||||
genHTMLReport bool
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func (r *HRPRunner) Run(testcases ...ITestCase) error
|
||||
func (r *HRPRunner) NewSessionRunner(testcase *TestCase) *SessionRunner
|
||||
```
|
||||
|
||||
重点关注两个方法:
|
||||
|
||||
- Run:测试执行的主入口,支持运行一个或多个测试用例
|
||||
- NewSessionRunner:针对给定的测试用例初始化一个 SessionRunner
|
||||
|
||||
### 用例执行器 SessionRunner
|
||||
|
||||
测试用例的具体执行都由 `SessionRunner` 完成,每个 TestCase 对应一个实例,在该实例中除了包含测试用例自身内容外,还会包含测试过程的 session 数据和最终测试结果 summary。
|
||||
|
||||
```go
|
||||
type SessionRunner struct {
|
||||
testCase *TestCase
|
||||
hrpRunner *HRPRunner
|
||||
parser *Parser
|
||||
sessionVariables map[string]interface{}
|
||||
transactions map[string]map[transactionType]time.Time
|
||||
startTime time.Time // record start time of the testcase
|
||||
summary *TestCaseSummary // record test case summary
|
||||
}
|
||||
```
|
||||
|
||||
重点关注一个方法:
|
||||
|
||||
- Start:启动执行用例,依次执行所有测试步骤
|
||||
|
||||
```go
|
||||
func (r *SessionRunner) Start() error {
|
||||
...
|
||||
// run step in sequential order
|
||||
for _, step := range r.testCase.TestSteps {
|
||||
_, err := step.Run(r)
|
||||
if err != nil && r.hrpRunner.failfast {
|
||||
return errors.Wrap(err, "abort running due to failfast setting")
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
在主流程中,SessionRunner 并不需要关注 step 的具体类型,统一都是调用 `step.Run(r)`,具体实现逻辑都在对应 step 的 `Run(*SessionRunner)` 方法中。
|
||||
@@ -173,13 +173,13 @@ func (v *responseObject) Validate(iValidators []interface{}, variablesMapping ma
|
||||
Msgf("validate %s", checkItem)
|
||||
if !result {
|
||||
v.t.Fail()
|
||||
return errors.New(fmt.Sprintf(
|
||||
"do assertion failed, checkExpr: %v, assertMethod: %v, checkValue: %v, expectValue: %v",
|
||||
validator.Check,
|
||||
assertMethod,
|
||||
checkValue,
|
||||
expectValue,
|
||||
))
|
||||
log.Error().
|
||||
Str("checkExpr", validator.Check).
|
||||
Str("assertMethod", assertMethod).
|
||||
Interface("checkValue", checkValue).
|
||||
Interface("expectValue", expectValue).
|
||||
Msg("assert failed")
|
||||
return errors.New("step validation failed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -69,24 +69,44 @@ func (r *SessionRunner) Start() error {
|
||||
r.startTime = time.Now()
|
||||
// run step in sequential order
|
||||
for _, step := range r.testCase.TestSteps {
|
||||
_, err := step.Run(r)
|
||||
log.Info().Str("step", step.Name()).
|
||||
Str("type", string(step.Type())).Msg("run step start")
|
||||
|
||||
stepResult, err := step.Run(r)
|
||||
if err != nil && r.hrpRunner.failfast {
|
||||
log.Error().
|
||||
Str("step", stepResult.Name).
|
||||
Str("type", string(stepResult.StepType)).
|
||||
Bool("success", false).
|
||||
Msg("run step end")
|
||||
return errors.Wrap(err, "abort running due to failfast setting")
|
||||
}
|
||||
|
||||
// update extracted variables
|
||||
r.updateSession(stepResult.ExportVars)
|
||||
// update testcase summary
|
||||
r.updateSummary(stepResult)
|
||||
|
||||
log.Info().
|
||||
Str("step", stepResult.Name).
|
||||
Str("type", string(stepResult.StepType)).
|
||||
Bool("success", stepResult.Success).
|
||||
Interface("exportVars", stepResult.ExportVars).
|
||||
Msg("run step end")
|
||||
}
|
||||
|
||||
log.Info().Str("testcase", config.Name).Msg("run testcase end")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SessionRunner) UpdateSession(vars map[string]interface{}) {
|
||||
func (r *SessionRunner) updateSession(vars map[string]interface{}) {
|
||||
for k, v := range vars {
|
||||
r.sessionVariables[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateSummary appends step result to summary
|
||||
func (r *SessionRunner) UpdateSummary(stepResult *StepResult) {
|
||||
// updateSummary appends step result to summary
|
||||
func (r *SessionRunner) updateSummary(stepResult *StepResult) {
|
||||
r.summary.Records = append(r.summary.Records, stepResult)
|
||||
r.summary.Stat.Total += 1
|
||||
if stepResult.Success {
|
||||
|
||||
@@ -3,8 +3,6 @@ package hrp
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/hrp/internal/builtin"
|
||||
)
|
||||
|
||||
@@ -93,8 +91,6 @@ func (s *StepAPIWithOptionalArgs) Struct() *TStep {
|
||||
}
|
||||
|
||||
func (s *StepAPIWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error) {
|
||||
log.Info().Str("api", s.step.Name).Msg("run referenced api")
|
||||
|
||||
// extend request with referenced API
|
||||
api, _ := s.step.API.(*API)
|
||||
extendWithAPI(s.step, api)
|
||||
|
||||
@@ -243,8 +243,6 @@ func (r *requestBuilder) prepareBody(stepVariables map[string]interface{}) error
|
||||
}
|
||||
|
||||
func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
|
||||
log.Info().Str("step", step.Name).Msg("run step start")
|
||||
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name,
|
||||
StepType: stepTypeRequest,
|
||||
@@ -255,18 +253,8 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
defer func() {
|
||||
// update testcase summary
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("run request step failed")
|
||||
stepResult.Attachment = err.Error()
|
||||
} else {
|
||||
// update extracted variables
|
||||
r.UpdateSession(stepResult.ExportVars)
|
||||
log.Info().
|
||||
Str("step", step.Name).
|
||||
Bool("success", stepResult.Success).
|
||||
Interface("exportVars", stepResult.ExportVars).
|
||||
Msg("run step end")
|
||||
}
|
||||
r.UpdateSummary(stepResult)
|
||||
}()
|
||||
|
||||
sessionData := newSessionData()
|
||||
|
||||
@@ -50,7 +50,6 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
|
||||
}
|
||||
s.step.Variables = stepVariables
|
||||
|
||||
log.Info().Str("testcase", s.step.Name).Msg("run referenced testcase")
|
||||
stepResult := &StepResult{
|
||||
Name: s.step.Name,
|
||||
StepType: stepTypeTestCase,
|
||||
@@ -73,8 +72,6 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
|
||||
err = sessionRunner.Start()
|
||||
stepResult.Elapsed = time.Since(start).Milliseconds()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("run referenced testcase step failed")
|
||||
log.Info().Str("step", s.step.Name).Bool("success", false).Msg("run step end")
|
||||
stepResult.Attachment = err.Error()
|
||||
r.summary.Success = false
|
||||
return stepResult, err
|
||||
@@ -96,12 +93,6 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
|
||||
r.summary.Stat.Successes += summary.Stat.Successes
|
||||
r.summary.Stat.Failures += summary.Stat.Failures
|
||||
|
||||
log.Info().
|
||||
Str("step", s.step.Name).
|
||||
Bool("success", true).
|
||||
Interface("exportVars", stepResult.ExportVars).
|
||||
Msg("run step end")
|
||||
|
||||
return stepResult, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,7 @@ func (s *StepThinkTime) Struct() *TStep {
|
||||
|
||||
func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
|
||||
thinkTime := s.step.ThinkTime
|
||||
log.Info().Str("name", s.step.Name).
|
||||
Float64("time", thinkTime.Time).Msg("think time")
|
||||
log.Info().Float64("time", thinkTime.Time).Msg("think time")
|
||||
|
||||
stepResult := &StepResult{
|
||||
Name: s.step.Name,
|
||||
|
||||
Reference in New Issue
Block a user