mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-10 17:43:00 +08:00
docs: update dev instruct
This commit is contained in:
@@ -34,8 +34,13 @@ type IStep interface {
|
||||
- [thinktime](step_thinktime.go):思考时间,按照配置的逻辑进行等待
|
||||
- [transaction](step_transaction.go):事务机制,用于压测
|
||||
- [rendezvous](step_rendezvous.go):集合点机制,用于压测
|
||||
- [mobile_UI](step_mobile_ui.go):移动端 UI 自动化
|
||||
- [websocket](step_websocket.go):WebSocket 通信
|
||||
- [android](step_ui.go):Android UI 自动化
|
||||
- [ios](step_ui.go):iOS UI 自动化
|
||||
- [harmony](step_ui.go):Harmony UI 自动化
|
||||
- [browser](step_ui.go):浏览器 UI 自动化
|
||||
- [shell](step_shell.go):执行 shell 命令
|
||||
- [function](step_function.go):自定义函数调用
|
||||
|
||||
基于该机制,我们可以扩展支持新的协议类型,例如 HTTP2/WebSocket/RPC 等;同时也可以支持新的测试类型,例如 UI 自动化。甚至我们还可以在一个测试用例中混合调用多种不同的 Step 类型,例如实现 HTTP/RPC/UI 混合场景。
|
||||
|
||||
@@ -43,28 +48,44 @@ type IStep interface {
|
||||
|
||||
### 整体控制器 HRPRunner
|
||||
|
||||
执行接口测试时,会初始化一个 `HRPRunner`,用于控制测试的执行策略。
|
||||
执行测试时,会初始化一个 `HRPRunner`,用于控制测试的执行策略。
|
||||
|
||||
```go
|
||||
type HRPRunner struct {
|
||||
t *testing.T
|
||||
failfast bool
|
||||
requestsLogOn bool
|
||||
pluginLogOn bool
|
||||
saveTests bool
|
||||
genHTMLReport bool
|
||||
client *http.Client
|
||||
t *testing.T
|
||||
failfast bool
|
||||
httpStatOn bool
|
||||
requestsLogOn bool
|
||||
pluginLogOn bool
|
||||
venv string
|
||||
saveTests bool
|
||||
genHTMLReport bool
|
||||
httpClient *http.Client
|
||||
http2Client *http.Client
|
||||
wsDialer *websocket.Dialer
|
||||
caseTimeoutTimer *time.Timer // case timeout timer
|
||||
interruptSignal chan os.Signal // interrupt signal channel
|
||||
}
|
||||
|
||||
func (r *HRPRunner) Run(testcases ...ITestCase) error
|
||||
func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error)
|
||||
func NewCaseRunner(testcase TestCase, hrpRunner *HRPRunner) (*CaseRunner, error)
|
||||
```
|
||||
|
||||
重点关注两个方法:
|
||||
重点关注的方法:
|
||||
|
||||
- Run:测试执行的主入口,支持运行一个或多个测试用例
|
||||
- NewCaseRunner:针对给定的测试用例初始化一个 CaseRunner
|
||||
|
||||
HRPRunner 支持多种配置选项:
|
||||
- SetFailfast:配置是否在步骤失败时立即停止
|
||||
- SetRequestsLogOn:开启请求响应详细日志
|
||||
- SetHTTPStatOn:开启 HTTP 延迟统计
|
||||
- SetPluginLogOn:开启插件日志
|
||||
- SetProxyUrl:配置代理 URL,用于抓包调试
|
||||
- SetRequestTimeout:配置全局请求超时
|
||||
- SetCaseTimeout:配置测试用例超时
|
||||
- GenHTMLReport:生成 HTML 测试报告
|
||||
|
||||
### 用例执行器 CaseRunner
|
||||
|
||||
针对每个测试用例,采用 CaseRunner 存储其公共信息,包括 plugin/parser
|
||||
@@ -96,49 +117,220 @@ type SessionRunner struct {
|
||||
|
||||
sessionVariables map[string]interface{} // testcase execution session variables
|
||||
summary *TestCaseSummary // record test case summary
|
||||
|
||||
// transactions stores transaction timing info.
|
||||
// key is transaction name, value is map of transaction type and time, e.g. start time and end time.
|
||||
transactions map[string]map[TransactionType]time.Time
|
||||
|
||||
// websocket session
|
||||
ws *wsSession
|
||||
}
|
||||
|
||||
func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCaseSummary, err error)
|
||||
func (r *SessionRunner) RunStep(step IStep) (stepResult *StepResult, err error)
|
||||
func (r *SessionRunner) ParseStep(step IStep) error
|
||||
```
|
||||
|
||||
重点关注一个方法:
|
||||
重点关注的方法:
|
||||
|
||||
- Start:启动执行用例,依次执行所有测试步骤
|
||||
- RunStep:执行单个测试步骤,支持循环执行
|
||||
- ParseStep:解析步骤配置,包括变量替换和验证器解析
|
||||
|
||||
```go
|
||||
func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCaseSummary, err error) {
|
||||
...
|
||||
r.resetSession()
|
||||
// report GA event
|
||||
sdk.SendGA4Event("hrp_session_runner_start", nil)
|
||||
|
||||
config := r.caseRunner.TestCase.Config.Get()
|
||||
log.Info().Str("testcase", config.Name).Msg("run testcase start")
|
||||
|
||||
// update config variables with given variables
|
||||
r.InitWithParameters(givenVars)
|
||||
|
||||
defer func() {
|
||||
// release session resources
|
||||
r.ReleaseResources()
|
||||
|
||||
summary = r.summary
|
||||
}
|
||||
summary.Name = config.Name
|
||||
summary.Time.Duration = time.Since(summary.Time.StartAt).Seconds()
|
||||
// ... handle export variables and logs
|
||||
}()
|
||||
|
||||
// run step in sequential order
|
||||
for _, step := range r.testCase.TestSteps {
|
||||
// parse step
|
||||
err = r.parseStepStruct(step)
|
||||
for _, step := range r.caseRunner.TestSteps {
|
||||
select {
|
||||
case <-r.caseRunner.hrpRunner.caseTimeoutTimer.C:
|
||||
log.Warn().Msg("timeout in session runner")
|
||||
return summary, errors.Wrap(code.TimeoutError, "session runner timeout")
|
||||
case <-r.caseRunner.hrpRunner.interruptSignal:
|
||||
log.Warn().Msg("interrupted in session runner")
|
||||
return summary, errors.Wrap(code.InterruptError, "session runner interrupted")
|
||||
default:
|
||||
_, err := r.RunStep(step)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
// interrupted or timeout, abort running
|
||||
if errors.Is(err, code.InterruptError) || errors.Is(err, code.TimeoutError) {
|
||||
return summary, err
|
||||
}
|
||||
|
||||
// run step
|
||||
stepResult, err := step.Run(r)
|
||||
|
||||
// update summary
|
||||
r.summary.Records = append(r.summary.Records, stepResult)
|
||||
|
||||
// update extracted variables
|
||||
for k, v := range stepResult.ExportVars {
|
||||
r.sessionVariables[k] = v
|
||||
}
|
||||
|
||||
// check if failfast
|
||||
if err != nil && r.caseRunner.hrpRunner.failfast {
|
||||
return errors.Wrap(err, "abort running due to failfast setting")
|
||||
// check if failfast
|
||||
if r.caseRunner.hrpRunner.failfast {
|
||||
return summary, errors.Wrap(err, "abort running due to failfast setting")
|
||||
}
|
||||
}
|
||||
}
|
||||
...
|
||||
|
||||
log.Info().Str("testcase", config.Name).Msg("run testcase end")
|
||||
return summary, nil
|
||||
}
|
||||
```
|
||||
|
||||
在主流程中,SessionRunner 并不需要关注 step 的具体类型,统一都是调用 `step.Run(r)`,具体实现逻辑都在对应 step 的 `Run(*SessionRunner)` 方法中。
|
||||
在主流程中,SessionRunner 并不需要关注 step 的具体类型,统一都是调用 `r.RunStep(step)`,具体实现逻辑都在对应 step 的 `Run(*SessionRunner)` 方法中。
|
||||
|
||||
## 新增特性
|
||||
|
||||
### 1. 超时和中断处理
|
||||
|
||||
v5 版本增加了完善的超时和中断处理机制:
|
||||
- 支持测试用例级别的超时控制
|
||||
- 支持优雅的中断处理(SIGTERM, SIGINT)
|
||||
- 在执行过程中实时检查超时和中断信号
|
||||
|
||||
### 2. 多平台 UI 自动化
|
||||
|
||||
统一的 UI 自动化接口,支持多个平台:
|
||||
- **Android**:基于 ADB 和 UIAutomator2
|
||||
- **iOS**:基于 WebDriverAgent (WDA)
|
||||
- **Harmony**:基于 HDC (Harmony Device Connector)
|
||||
- **Browser**:基于 WebDriver 协议
|
||||
|
||||
### 3. AI 集成
|
||||
|
||||
集成了大模型能力:
|
||||
- 支持 AI 驱动的 UI 操作
|
||||
- 通过 MCP (Model Context Protocol) 与大模型通信
|
||||
- 支持自然语言描述的测试步骤
|
||||
|
||||
### 4. 增强的步骤配置
|
||||
|
||||
步骤配置支持更多选项:
|
||||
```go
|
||||
type StepConfig struct {
|
||||
StepName string `json:"name" yaml:"name"` // required
|
||||
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
|
||||
SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"`
|
||||
TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"`
|
||||
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 *types.IntOrString `json:"loops,omitempty" yaml:"loops,omitempty"`
|
||||
IgnorePopup bool `json:"ignore_popup,omitempty" yaml:"ignore_popup,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 协议支持扩展
|
||||
|
||||
除了 HTTP/HTTPS,还支持:
|
||||
- HTTP/2 协议
|
||||
- WebSocket 通信
|
||||
- 自定义函数调用
|
||||
|
||||
### 6. 资源管理
|
||||
|
||||
增强的资源管理机制:
|
||||
- 自动释放会话资源
|
||||
- UI 驱动器缓存管理
|
||||
- 日志收集和聚合
|
||||
|
||||
## UI 自动化步骤示例
|
||||
|
||||
### StepMobile 结构
|
||||
|
||||
UI 自动化步骤统一使用 `StepMobile` 结构:
|
||||
|
||||
```go
|
||||
type StepMobile struct {
|
||||
StepConfig
|
||||
Mobile *MobileUI `json:"mobile,omitempty" yaml:"mobile,omitempty"`
|
||||
Android *MobileUI `json:"android,omitempty" yaml:"android,omitempty"`
|
||||
Harmony *MobileUI `json:"harmony,omitempty" yaml:"harmony,omitempty"`
|
||||
IOS *MobileUI `json:"ios,omitempty" yaml:"ios,omitempty"`
|
||||
Browser *MobileUI `json:"browser,omitempty" yaml:"browser,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### 常用 UI 操作方法
|
||||
|
||||
```go
|
||||
// 基础操作
|
||||
func (s *StepMobile) TapXY(x, y float64, opts ...option.ActionOption) *StepMobile
|
||||
func (s *StepMobile) TapByOCR(ocrText string, opts ...option.ActionOption) *StepMobile
|
||||
func (s *StepMobile) TapByCV(imagePath string, opts ...option.ActionOption) *StepMobile
|
||||
func (s *StepMobile) AIAction(prompt string, opts ...option.ActionOption) *StepMobile
|
||||
|
||||
// 应用管理
|
||||
func (s *StepMobile) AppLaunch(bundleId string) *StepMobile
|
||||
func (s *StepMobile) AppTerminate(bundleId string) *StepMobile
|
||||
func (s *StepMobile) InstallApp(path string) *StepMobile
|
||||
|
||||
// 滑动操作
|
||||
func (s *StepMobile) Swipe(sx, sy, ex, ey float64, opts ...option.ActionOption) *StepMobile
|
||||
func (s *StepMobile) SwipeUp(opts ...option.ActionOption) *StepMobile
|
||||
func (s *StepMobile) SwipeDown(opts ...option.ActionOption) *StepMobile
|
||||
|
||||
// 输入操作
|
||||
func (s *StepMobile) Input(text string, opts ...option.ActionOption) *StepMobile
|
||||
|
||||
// 等待操作
|
||||
func (s *StepMobile) Sleep(nSeconds float64, startTime ...time.Time) *StepMobile
|
||||
func (s *StepMobile) SleepRandom(params ...float64) *StepMobile
|
||||
|
||||
// 验证操作
|
||||
func (s *StepMobile) Validate() *StepMobileUIValidation
|
||||
```
|
||||
|
||||
### UI 验证方法
|
||||
|
||||
```go
|
||||
// OCR 文本验证
|
||||
func (s *StepMobileUIValidation) AssertOCRExists(expectedText string, msg ...string) *StepMobileUIValidation
|
||||
func (s *StepMobileUIValidation) AssertOCRNotExists(expectedText string, msg ...string) *StepMobileUIValidation
|
||||
|
||||
// 图像验证
|
||||
func (s *StepMobileUIValidation) AssertImageExists(expectedImagePath string, msg ...string) *StepMobileUIValidation
|
||||
func (s *StepMobileUIValidation) AssertImageNotExists(expectedImagePath string, msg ...string) *StepMobileUIValidation
|
||||
|
||||
// AI 验证
|
||||
func (s *StepMobileUIValidation) AssertAI(prompt string, msg ...string) *StepMobileUIValidation
|
||||
|
||||
// 应用状态验证
|
||||
func (s *StepMobileUIValidation) AssertAppInForeground(packageName string, msg ...string) *StepMobileUIValidation
|
||||
func (s *StepMobileUIValidation) AssertAppNotInForeground(packageName string, msg ...string) *StepMobileUIValidation
|
||||
```
|
||||
|
||||
## 开发建议
|
||||
|
||||
### 1. 添加新的步骤类型
|
||||
|
||||
要添加新的步骤类型,需要:
|
||||
1. 在 `step.go` 中定义新的 `StepType` 常量
|
||||
2. 创建实现 `IStep` 接口的结构体
|
||||
3. 在 `testcase.go` 的 `loadISteps` 方法中添加对应的处理逻辑
|
||||
|
||||
### 2. 扩展 UI 平台支持
|
||||
|
||||
要支持新的 UI 平台:
|
||||
1. 在 `uixt/` 目录下实现对应的驱动器
|
||||
2. 在 `StepMobile` 中添加新的平台字段
|
||||
3. 在 `obj()` 方法中添加对应的处理逻辑
|
||||
|
||||
### 3. 调试技巧
|
||||
|
||||
- 使用 `SetRequestsLogOn()` 开启详细的请求日志
|
||||
- 使用 `SetPluginLogOn()` 开启插件日志
|
||||
- 使用 `SetProxyUrl()` 配置代理进行抓包分析
|
||||
- 查看生成的 HTML 报告了解执行详情
|
||||
|
||||
@@ -1 +1 @@
|
||||
v5.0.0-beta-2505271528
|
||||
v5.0.0-beta-2505271534
|
||||
|
||||
Reference in New Issue
Block a user