- Add AutoPopupHandler field to both TConfig and StepConfig - Support testcase-level global configuration via TConfig.EnableAutoPopupHandler() - Support step-level specific configuration via StepMobile.EnableAutoPopupHandler() - Priority: testcase config > step config > default disabled - Simplify Loops field type from *types.IntOrString to int in StepConfig - Update documentation to reflect new structure
12 KiB
代码阅读指南
核心数据结构
HttpRunner 以 TestCase 为核心,将任意测试场景抽象为有序步骤的集合。
type TestCase struct {
Config IConfig `json:"config" yaml:"config"`
TestSteps []IStep `json:"teststeps" yaml:"teststeps"`
}
type IConfig interface {
Get() *TConfig
}
其中,测试步骤 IStep 采用了 go interface 的设计理念,支持进行任意拓展;步骤内容统一在 Run 方法中进行实现。
type IStep interface {
Name() string
Type() StepType
Config() *StepConfig
Run(*SessionRunner) (*StepResult, error)
}
我们只需遵循 IStep 的接口定义,即可实现各种类型的测试步骤类型。当前 hrp 已支持的步骤类型包括:
- request:发起单次 HTTP 请求
- api:引用执行其它 API 文件
- testcase:引用执行其它测试用例文件
- thinktime:思考时间,按照配置的逻辑进行等待
- transaction:事务机制,用于压测
- rendezvous:集合点机制,用于压测
- websocket:WebSocket 通信
- android:Android UI 自动化
- ios:iOS UI 自动化
- harmony:Harmony UI 自动化
- browser:浏览器 UI 自动化
- shell:执行 shell 命令
- function:自定义函数调用
基于该机制,我们可以扩展支持新的协议类型,例如 HTTP2/WebSocket/RPC 等;同时也可以支持新的测试类型,例如 UI 自动化。甚至我们还可以在一个测试用例中混合调用多种不同的 Step 类型,例如实现 HTTP/RPC/UI 混合场景。
运行主流程
整体控制器 HRPRunner
执行测试时,会初始化一个 HRPRunner,用于控制测试的执行策略。
type HRPRunner struct {
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 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
type CaseRunner struct {
TestCase // each testcase init its own CaseRunner
hrpRunner *HRPRunner // all case runners share one HRPRunner
parser *Parser // each CaseRunner init its own Parser
parametersIterator *ParametersIterator
}
func (r *CaseRunner) NewSession() *SessionRunner
重点关注一个方法:
- NewSession:测试用例的每一次执行对应一个 SessionRunner
SessionRunner
测试用例的具体执行都由 SessionRunner 完成,每个 session 实例中除了包含测试用例自身内容外,还会包含测试过程的 session 数据和最终测试结果 summary。
type SessionRunner struct {
caseRunner *CaseRunner // all session runners share one CaseRunner
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:解析步骤配置,包括变量替换和验证器解析
func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCaseSummary, err error) {
// 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.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
}
// 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 的具体类型,统一都是调用 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. 增强的步骤配置
步骤配置支持更多选项:
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"`
AutoPopupHandler bool `json:"auto_popup_handler,omitempty" yaml:"auto_popup_handler,omitempty"` // enable auto popup handler for this step
}
5. 协议支持扩展
除了 HTTP/HTTPS,还支持:
- HTTP/2 协议
- WebSocket 通信
- 自定义函数调用
6. 资源管理
增强的资源管理机制:
- 自动释放会话资源
- UI 驱动器缓存管理
- 日志收集和聚合
UI 自动化步骤示例
StepMobile 结构
UI 自动化步骤统一使用 StepMobile 结构:
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 操作方法
// 基础操作
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 验证方法
// 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. 添加新的步骤类型
要添加新的步骤类型,需要:
- 在
step.go中定义新的StepType常量 - 创建实现
IStep接口的结构体 - 在
testcase.go的loadISteps方法中添加对应的处理逻辑
2. 扩展 UI 平台支持
要支持新的 UI 平台:
- 在
uixt/目录下实现对应的驱动器 - 在
StepMobile中添加新的平台字段 - 在
obj()方法中添加对应的处理逻辑
3. 调试技巧
- 使用
SetRequestsLogOn()开启详细的请求日志 - 使用
SetPluginLogOn()开启插件日志 - 使用
SetProxyUrl()配置代理进行抓包分析 - 查看生成的 HTML 报告了解执行详情