源码

官网

demo 账号:admin,密码:123456

我们通过调用HRPRunner.Run来执行测试用例。在执行之前,让我们先了解一下HRPRunner所包含的内容,因为这些内容将用于后续的运行配置。大多数参数在运行前需要进行确认。

HRPRunner是一个执行器,它封装了用例的运行过程。在运行测试用例之前,我们需要确认并设置许多参数,这些参数将影响测试的执行。这些参数可以包括用例的路径、配置、运行模式等。通过在HRPRunner中设置这些参数,我们可以在运行过程中按照预期配置和执行测试用例,从而得到正确的结果和报告。

理解HRPRunner的内容和参数设置,是保证测试用例按照预期执行的关键。因此,在执行测试用例之前,我们需要仔细了解这些参数,并进行必要的设置,以确保测试的准确性和可靠性。

type HRPRunner struct {
    t             *testing.T         // testing.T的引用,用于报告测试失败和管理测试状态
    failfast      bool               // 是否在第一个测试失败后立即停止测试的标志。如果设置为true,则在第一个失败后测试停止,否则会继续执行所有测试。
    httpStatOn    bool               // 是否启用HTTP统计跟踪的标志。如果设置为true,则在测试执行期间收集并显示与HTTP请求相关的统计信息。
    requestsLogOn bool               // 是否启用HTTP请求日志的标志。如果设置为true,则在测试执行期间记录每个HTTP请求的详细信息。
    pluginLogOn   bool               // 是否启用插件日志的标志。如果设置为true,则在测试执行期间记录与插件相关的信息。
    venv          string             // 虚拟环境路径的字符串。用于指定测试将在其中执行的虚拟环境的路径。
    saveTests     bool               // 是否保存测试结果的标志。如果设置为true,则将保存测试结果。
    genHTMLReport bool               // 是否生成测试执行的HTML报告的标志。如果设置为true,则会生成总结测试结果的HTML报告。
    httpClient    *http.Client       // 指向HTTP客户端实例的指针。用于在测试执行期间进行HTTP请求。
    http2Client   *http.Client       // 指向HTTP/2客户端实例的指针。用于在测试执行期间进行HTTP/2请求。
    wsDialer      *websocket.Dialer  // 指向WebSocket拨号器实例的指针。用于在测试执行期间建立WebSocket连接。
    uiClients     map[string]*uixt.DriverExt  // UI客户端的映射。用于管理具有唯一键作为标识符的UI测试驱动程序。
}

这部分内容实际上也属于配置,不同之处在于这里配置的是整个测试的运行环境。相比之下,config更侧重于每个测试用例的个别配置。由于我们可以同时运行多个测试用例,因此将整体的配置分为运行配置和用例配置两个部分。

在实际运行时,我们首先会创建一个HRPRunner对象,然后通过调用其Run方法来开始执行测试用例。下面我们将重点介绍测试用例的运行过程。这个过程中,HRPRunner会根据之前设置的运行配置和各个测试用例的个别配置,按照既定方式执行测试,并生成相应的结果报告。这个过程是整个自动化测试框架的核心,它确保了测试用例能够在正确的环境中运行,并输出准确的结果,为测试提供了稳定可靠的支持。

// Run starts to execute one or multiple testcases.
// 运行开始执行一个或多个测试用例。
func (r *HRPRunner) Run(testcases ...ITestCase) error {
    // 输出HRP版本号
    log.Info().Str("hrp_version", version.VERSION).Msg("start running")
    // 初始化事件跟踪对象,用于统计执行过程中的事件
    event := sdk.EventTracking{
        Category: "RunAPITests",
        Action:   "hrp run",
    }
    go sdk.SendEvent(event)
    defer sdk.SendEvent(event.StartTiming("execution"))
    // 记录执行数据到总结对象中
    s := newOutSummary()

    // 加载所有测试用例
    testCases, err := LoadTestCases(testcases...)
    if err != nil {
        log.Error().Err(err).Msg("failed to load testcases")
        return err
    }

    // 退出所有函数插件
    defer func() {
        pluginMap.Range(func(key, value interface{}) bool {
            if plugin, ok := value.(funplugin.IPlugin); ok {
                plugin.Quit()
            }
            return true
        })
    }()

    var runErr error
    // 遍历所有用例
    for _, testcase := range testCases {
        // 每个测试用例都有自己的用例运行器,把用例转换成可迭代的用例
        caseRunner, err := r.NewCaseRunner(testcase)
        if err != nil {
            log.Error().Err(err).Msg("[Run] init case runner failed")
            return err
        }

        // 释放UI驱动程序的会话
        defer func() {
            for _, client := range r.uiClients {
                client.Driver.DeleteSession()
            }
        }()

        // 使用参数迭代器执行用例多次,每次执行有不同的参数
        for it := caseRunner.parametersIterator; it.HasNext(); {
            // 每次运行都有自己的会话运行器
            sessionRunner := caseRunner.NewSession()
            err1 := sessionRunner.Start(it.Next())
            if err1 != nil {
                log.Error().Err(err1).Msg("[Run] run testcase failed")
                runErr = err1
            }
            // 获取用例的执行结果
            caseSummary, err2 := sessionRunner.GetSummary()
            s.appendCaseSummary(caseSummary)
            if err2 != nil {
                log.Error().Err(err2).Msg("[Run] get summary failed")
                if err1 != nil {
                    runErr = errors.Wrap(err1, err2.Error())
                } else {
                    runErr = err2
                }
            }

            // 如果发生错误且设置了failfast标志,则终止运行
            // 实际操作一般都会执行所有用例后,最后获取用例执行情况
            if runErr != nil && r.failfast {
                break
            }
        }
    }

    // 计算执行时间
    s.Time.Duration = time.Since(s.Time.StartAt).Seconds()

    // 保存测试报告
    if r.saveTests {
        err := s.genSummary()
        if err != nil {
            return err
        }
    }

    // 生成HTML报告
    if r.genHTMLReport {
        err := s.genHTMLReport()
        if err != nil {
            return err
        }
    }

    return runErr
}

整个运行过程可以分为以下五个主要部分:

  1. 事件跟踪:这部分内容主要用于记录测试执行的情况,不会对实际执行产生影响。它帮助我们跟踪测试的进度和执行细节。
  2. 加载用例:在前面的文章中已经介绍过,这一部分涉及将测试用例从文件中读取并加载到内存中,为后续执行做准备。
  3. 遍历用例并执行:这是整个自动化测试的核心步骤。在这一步,系统会遍历已加载的测试用例,逐个执行它们。这个过程中,测试框架会根据运行配置和用例配置,按照预定的操作顺序执行测试步骤、发起请求、验证结果等。
  4. 统计执行事件:在测试用例执行过程中,会涉及到许多事件,如请求时间、断言结果、异常处理等。这一部分会将这些事件进行统计和记录,为生成测试报告提供数据支持。
  5. 生成测试报告:最后一步是生成测试报告。在所有测试用例执行完毕后,系统会根据执行情况、统计数据和配置信息,生成详细的测试报告。这个报告会展示每个测试用例的执行结果、耗时、断言结果等,帮助用户全面了解测试的执行情况。

这五个部分共同构成了整个测试框架的执行流程,确保测试用例能够在正确的环境中执行,并为用户提供准确、详尽的测试报告。


↙↙↙阅读原文可查看相关链接,并与作者交流