【测试平台系列】第一章 手撸压力机(十)-定义场景

简介: 上一章,咱们对http请求进行了一些优化,本章节我们将组成场景去运行。首先场景就是一连串的http接口的请求,我们使用list(列表)来组装成一个场景

上一章,咱们对http请求进行了一些优化,本章节我们将组成场景去运行。首先场景就是一连串的http接口的请求,我们使用list(列表)来组装成一个场景。
首先我们在model目录下,新建test_scene.go文件。

// Package model -----------------------------
// @file      : test_scene.go
// @author    : 被测试耽误的大厨
// @contact   : 13383088061@163.com
// @time      : 2023/7/6 15:53
// -------------------------------------------
package model

import "sync"

// TestScene 业务流程组成的测试场景
type TestScene struct {
        Name        string       `json:"name"`         // 场景名称
        Id          string       `json:"id"`           // 唯一id
        ParentId    string       `json:"parent_id"`    // 父id
        ItemId      string       `json:"item_id"`      // 项目Id
        TeamId      string       `json:"team_id"`      // 团队Id
        SourceId    string       `json:"source_id"`    // 源Id
        ChannelId   string       `json:"channel_id"`   // 渠道Id比如YApi,postman等
        ChannelType string       `json:"channel_type"` // 渠道类型
        TestObjects []TestObject `json:"test_objects"` // 场景
}

/*
 处理测试场景
*/

func (testScene TestScene) Dispose() {
        // 从testScene.TestObjects的最外层开始循环
        for _, testObject := range testScene.TestObjects {
                // 声明一个waitGroup控制等待内层循环请求内部完成
                wg := sync.WaitGroup{}
                // 从一层级的list中读取每个TestObject对象
                wg.Add(1)
                go func(object TestObject) {
                        defer wg.Done()
                        response := TestObjectResponse{
                                Name:        object.Name,
                                Id:          object.Id,
                                SceneId:     testScene.Id,
                ItemId:      object.ItemId,
                                ObjectType:  object.ObjectType,

                        }
                        // 开始进行请求处理
                        object.Dispose(&response)
                }(testObject)
                // 等待内层的list完成后,再开始下一次外层循环
                wg.Wait()
        }
}

上面的内容,我们定义了一个TestScene结构体,来表示我们的测试场景,然后定义了TestScene的结构体来处理场景。
然后我们新建一个reponse_data.go文件,用来声明我们需要的测试结果信息


// Package model -----------------------------
// @file      : response_data.go
// @author    : 被测试耽误的大厨
// @contact   : 13383088061@163.com
// @time      : 2023/7/6 16:21
// -------------------------------------------
package model

// TestObjectResponse 响应信息, 用于返回给调用方
type TestObjectResponse struct {
        Name            string            `json:"name"`         // 对象名称
        Id              string            `json:"id"`           // 对象id
        SceneId         string            `json:"scene_id"`     // 场景id
        ObjectType      string            `json:"object_type"`  // 对象类型http、websocket、dubbo等
        ItemId          string            `json:"item_id"`      // 项目Id
        TeamId          string            `json:"team_id"`      // 团队Id
        Code            int               `json:"code"`
        RequestHeaders  map[string]string `json:"request_headers"`
        ResponseHeaders map[string]string `json:"response_headers"`
        Response        string            `json:"response"`
        RequestTime     int64             `json:"request_time"`
}

我们将原来test_object.go中的结构体单独提取到reponse_data.go文件中,并对一些无用的字段进行优化。

// Package model -----------------------------
// @file      : test_object.go
// @author    : 被测试耽误的大厨
// @contact   : 13383088061@163.com
// @time      : 2023/6/11 20:38
// -------------------------------------------
package model

// TestObject 测试对象结构体
type TestObject struct {
        Name        string      `json:"name"`        // 对象名称
        Id          string      `json:"id"`          // 唯一id
        ObjectType  string      `json:"object_type"` // 对象类型http、websocket、dubbo等
        ItemId      string      `json:"item_id"`     // 项目Id
        TeamId      string      `json:"team_id"`     // 团队Id
        HttpRequest HttpRequest `json:"http_request"`
}

// Dispose 测试对象的处理函数,在go语言中 Dispose方法是TestObject对象的方法,其他对象不能使用
func (to TestObject) Dispose(response *TestObjectResponse) {
        switch to.ObjectType {
        case HTTP1: // 由于我们有个多类型,为了方便统计,我们定义好变量,直接进行比对即可
                to.HttpRequest.Request(response)
                return
        }
        return
}

然后我们在service目录下新建scene_api.go文件,新建RunTestScene方法来实现请求读取。

// Package service -----------------------------
// @file      : scene_api.go
// @author    : 被测试耽误的大厨
// @contact   : 13383088061@163.com
// @time      : 2023/7/6 16:06
// -------------------------------------------
package service

import (
        "encoding/json"
        "fmt"
        "github.com/gin-gonic/gin"
        "github.com/google/uuid"
        "kitchen-engine/global/common"
        "kitchen-engine/global/log"
        "kitchen-engine/model"
        "net/http"
)

/*
        RunTestScene 实现run/testObject/接口
*/

func RunTestScene(c *gin.Context) {

        // 声明一个TO对象
        var testScene model.TestScene

        // 接收json格式的请求数据
        err := c.ShouldBindJSON(&testScene)
        id := uuid.New().String()
        // 如果请求格式错误
        if err != nil {
                log.Logger.Error("请求数据格式错误", err.Error())
                common.ReturnResponse(c, http.StatusBadRequest, id, "请求数据格式错误!", err.Error())
                return
        }

        // 使用json包解析以下TO对象, 解析出来为[]byte类型
        requestJson, _ := json.Marshal(testScene)
        // 打印以下日志, 使用fmt.Sprintf包格式花数据,%s 表示string(requestJson)为字符串类型,如果不确定类型,可以使用%v表示
        log.Logger.Debug(fmt.Sprintf("测试对象: %s", string(requestJson)))

        // 如果场景为空,直接返回
        if testScene.TestObjects == nil || len(testScene.TestObjects) == 0 {
                common.ReturnResponse(c, http.StatusBadRequest, id, "请求错误!", "场景不能为空")
                return
        }

        // 开始处理TO
        testScene.Dispose()
        // 返回响应消息
        common.ReturnResponse(c, http.StatusOK, id, "请求成功!", "")
        return
}

同时,将service目录下的api.go更名为object_api.go,并对其中的无用字段进行优化:

// Package service -----------------------------
// @file      : object_api.go
// @author    : 被测试耽误的大厨
// @contact   : 13383088061@163.com
// @time      : 2023/7/4 18:05
// -------------------------------------------
package service

import (
        "encoding/json"
        "fmt"
        "github.com/gin-gonic/gin"
        "github.com/google/uuid"
        "kitchen-engine/global/common"
        "kitchen-engine/global/log"
        "kitchen-engine/model"
        "net/http"
)

/*
        RunTestObject 实现run/testObject/接口
*/

func RunTestObject(c *gin.Context) {

        // 声明一个TO对象
        var testObject model.TestObject

        // 接收json格式的请求数据
        err := c.ShouldBindJSON(&testObject)
        id := uuid.New().String()
        // 如果请求格式错误
        if err != nil {
                log.Logger.Error("请求数据格式错误", err.Error())
                common.ReturnResponse(c, http.StatusBadRequest, id, "请求数据格式错误!", err.Error())
                return
        }

        // 使用json包解析以下TO对象, 解析出来为[]byte类型
        requestJson, _ := json.Marshal(testObject)
        // 打印以下日志, 使用fmt.Sprintf包格式花数据,%s 表示string(requestJson)为字符串类型,如果不确定类型,可以使用%v表示
        log.Logger.Debug(fmt.Sprintf("测试对象: %s", string(requestJson)))

        response := model.TestObjectResponse{
                Name:       testObject.Name,
                Id:         testObject.Id,
                ObjectType: testObject.ObjectType,
                ItemId:     testObject.ItemId,
        }

        // 开始处理TO
        testObject.Dispose(&response)
        // 返回响应消息
        common.ReturnResponse(c, http.StatusOK, id, "请求成功!", response)
        return
}

我们再对Http请求添加一个timeout字段,并在发送请求是设置超时时间。

// HttpRequest http请求的结构
type HttpRequest struct {
        Url                string             `json:"url"`                  // 接口uri
        Method             string             `json:"method"`               // 接口方法,Get Post Update...
        RequestTimeout     int64              `json:"request_timeout"`      // 请求超时时长,默认为30秒,防止请求阻塞
        Headers            []Header           `json:"headers"`              // 接口请求头
        Querys             []Query            `json:"querys"`               // get请求时的url
        Cookies            []Cookie           `json:"cookies"`              // cookie
        Body               *Body              `json:"body"`                 // 请求提
        HttpClientSettings HttpClientSettings `json:"http_client_settings"` // http客户端配置
}
在http_request.go文件中,新建HttpRequest结构体的setReqTime方法
// 设置请求超时时间
func (hr *HttpRequest) setReqTimeout(req *fasthttp.Request) {
        if hr.RequestTimeout <= 0 {
                hr.RequestTimeout = 30
        }
        req.SetTimeout(time.Duration(hr.RequestTimeout) * time.Second)
}

并对HttpRequest结构体的Request方法进行优化
func (hr *HttpRequest) Request(response *TestObjectResponse) {

        // 使用fasthttp 协程池

        // 新建一个http请求
        req := fasthttp.AcquireRequest()
        defer fasthttp.ReleaseRequest(req)
        // 新建一个http响应接受服务端的返回
        resp := fasthttp.AcquireResponse()
        defer fasthttp.ReleaseResponse(resp)

        // 新建一个http的客户端, newHttpClient是一个方法,在下面
        client := newHttpClient(hr.HttpClientSettings)

        // 设置请求方法
        hr.methodInit(req)

        // 设置header
        hr.headerInit(req)
        // 设置query
        hr.queryInit(req)
        // 设置cookie
        hr.cookieInit(req)

        // 设置url
        hr.urlInit(req)

        // 设置body
        hr.bodyInit(req)

        // 设置请求超时时间
        hr.setReqTimeout(req)

        // 记录开始时间
        startTime := time.Now()
        // 开始请求
        err := client.Do(req, resp)
        // 计算响应时间差值
        requestTime := time.Since(startTime)
        response.RequestTime = requestTime.Milliseconds()
        response.Code = resp.StatusCode()
        if err != nil {
                response.Response = err.Error()
                return
        }

        // 如果响应中有压缩类型,防止压缩类型出现乱码
        switch string(resp.Header.ContentEncoding()) {
        case "br", "deflate", "gzip":
                b, _ := resp.BodyUncompressed()
                response.Response = string(b)
        default:
                response.Response = string(resp.Body())
        }

}

同时,我们添加run/testScene接口,修改router.go中的initRouters方法。


/*
  路由配置
*/
func initRouters(groups *gin.RouterGroup) {
        {
                groups.POST("/run/testObject/", service.RunTestObject) //运行测试对象接口, url: http://*****/engine/run/testObject/
                groups.POST("/run/testScene/", service.RunTestScene)   //运行测试对象接口, url: http://*****/engine/run/testObject/

        }

}

这样,我们对场景的简单封装就完成了,下一步我们将对场景以及请求进行更全面的适配。

相关文章
|
10月前
|
关系型数据库 MySQL 测试技术
【分享】AgileTC测试用例管理平台使用分享
AgileTC 是一个脑图样式测试用例管理平台,支持用例设计、执行与团队协作,帮助测试人员高效管理测试流程。
491 116
【分享】AgileTC测试用例管理平台使用分享
|
10月前
|
人工智能 数据可视化 测试技术
AI测试平台自动遍历:低代码也能玩转全链路测试
AI测试平台的自动遍历功能,通过低代码配置实现Web和App的自动化测试。用户只需提供入口链接或安装包及简单配置,即可自动完成页面结构识别、操作验证,并生成可视化报告,大幅提升测试效率,特别适用于高频迭代项目。
|
10月前
|
人工智能 测试技术 调度
写用例写到怀疑人生?AI 智能测试平台帮你一键生成!
霍格沃兹测试开发学社推出AI智能测试用例生成功能,结合需求文档一键生成高质量测试用例,大幅提升效率,减少重复劳动。支持自定义提示词、多文档分析与批量管理,助力测试人员高效完成测试设计,释放更多时间投入核心分析工作。平台已开放内测,欢迎体验!
|
10月前
|
人工智能 测试技术 项目管理
测试不再碎片化:AI智能体平台「项目资料套件」功能上线!
在实际项目中,需求文档分散、整理费时、测试遗漏等问题常困扰测试工作。霍格沃兹推出AI智能体测试平台全新功能——项目资料套件,可将多个关联文档打包管理,并一键生成测试用例,提升测试完整性与效率。支持套件创建、文档关联、编辑删除及用例生成,适用于复杂项目、版本迭代等场景,助力实现智能化测试协作,让测试更高效、更专业。
|
11月前
|
存储 人工智能 算法
AI测试平台实战:深入解析自动化评分和多模型对比评测
在AI技术迅猛发展的今天,测试工程师面临着如何高效评估大模型性能的全新挑战。本文将深入探讨AI测试平台中自动化评分与多模型对比评测的关键技术与实践方法,为测试工程师提供可落地的解决方案。
|
存储 测试技术 虚拟化
VMmark 4.0.3 - 虚拟化平台基准测试
VMmark 4.0.3 - 虚拟化平台基准测试
373 0
VMmark 4.0.3 - 虚拟化平台基准测试
|
10月前
|
人工智能 边缘计算 搜索推荐
AI产品测试学习路径全解析:从业务场景到代码实践
本文深入解析AI测试的核心技能与学习路径,涵盖业务理解、模型指标计算与性能测试三大阶段,助力掌握分类、推荐系统、计算机视觉等多场景测试方法,提升AI产品质量保障能力。
|
10月前
|
人工智能 自然语言处理 测试技术
AI测试平台的用例管理实践:写得清晰,管得高效,执行更智能
在测试过程中,用例分散、步骤模糊、回归测试效率低等问题常困扰团队。霍格沃兹测试开发学社推出的AI测试平台,打通“用例编写—集中管理—智能执行”全流程,提升测试效率与覆盖率。平台支持标准化用例编写、统一管理操作及智能执行,助力测试团队高效协作,释放更多精力优化测试策略。目前平台已开放内测,欢迎试用体验!
|
11月前
|
存储 人工智能 文字识别
从零开始打造AI测试平台:文档解析与知识库构建详解
AI时代构建高效测试平台面临新挑战。本文聚焦AI问答系统知识库建设,重点解析文档解析关键环节,为测试工程师提供实用技术指导和测试方法论
|
11月前
|
人工智能 安全 数据可视化
安全测试平台的选型标准与搭建思路
随着企业安全需求升级,传统漏洞扫描和渗透测试已无法满足要求。构建安全测试平台(STP)成为趋势,实现漏洞扫描、权限评估、接口测试等工作的平台化运营。本文从选型标准、平台架构、模块功能等六个方面,系统讲解如何搭建企业级安全测试平台,提升安全能力。