目录
背景
之前写过一篇博客,介绍怎么用Python通过解析抓包数据,完成自动化用例的编写。最近这段时间在使用go test,所以就在想能不能也使用代码来生成自动化用例,快速提升测试用例覆盖率。说干就干。
框架
首先介绍一下我们使用的测框架:
项 | 信息 | 安装 | 备注 |
---|---|---|---|
GO版本 | go1.12.9 darwin/amd64 | 略 | |
测试框架 | ginkgo | go get -u github.com/onsi/ginkgo/ginkgo | |
断言库 | testify/assert | go get github.com/stretchr/testify | 官方配套的断言库是gomega |
ginkgo初始化
- 初始化:
cd path/to/package/you/want/to/test && ginkgo bootstrap
- 创建示例用例:
ginkgo generate
(需要手动添加测试用例) - 运行测试:
go test
orginkgo
注:-v
加上参数可打印运行信息
抓包&运行脚本
- 使用抓包工具(如Charles)抓包,把数据包导出为har格式,保存在当前目录下
- 如何安装抓包工具在本文就不赘述了,抓包,过滤出想要的数据,导出,保存的格式注意选择为
har
:
- 如何安装抓包工具在本文就不赘述了,抓包,过滤出想要的数据,导出,保存的格式注意选择为
- 根据实际情况修改全局变量信息,如bizBaseFolder、serverName、userFile等
- 使用
go run gentest.go
运行脚本即可
目录说明
然后我们一起来了解一下我们的目录结构定义。
∮./business
业务封装,封装具体的请求及测试数据
∮./conf
配置信息及接口请求参数初始化封装
∮./utils
公共函数封装
∮./testcase
接口测试用例目录
testcase 用例目录结构规则
基本原则: 根据项目、模块、接口功能逐级区分,建议最多3层目录层级
¶示例
- 软件测试论坛项目组/论坛项目/帖子模块/创建帖子接口:
- CN_TestBBS/bbs/post/post_test.go
- 基础账号项目/首页项目/白名单接口:
- CN_account/homepage/whitelist_test.go
实现思路
按照har文件的JSON结构定义对应的结构体,然后解析数据,生成请求数据,生成断言数据,初始化测试套suite,格式化代码,初始化包引用信息。
解析Har数据
定义结构体
Log struct {
version string
creator string
Entries []struct {
startedDateTime string
time string
Request struct {
...
解析到json
func UnpackHar(har []byte) (logs *Har) {
err := json.Unmarshal(har, &logs)
if err != nil {
fmt.Println(err)
}
return
}
转换请求数据
转换请求
转换请求参数
GET
// 格式化请求参数为标准请求string
getReqParam := make(map[string]interface{}, 1)
if len(v.Request.QueryString) > 0 {
for _, query := range v.Request.QueryString {
getReqParam[query.Name] = query.Value
}
}
// 获取postReq数据
postReqParamStr := v.Request.PostData.Text
if v.Request.Method == "GET" {
paramstr = genGetParam(InterfaceName, getReqParam)
}
func genGetParam(interfaceName string, param map[string]interface{}) (formatParam string) {
// 对于请求参数的value值为 数组
if len(param) > 0 {
for k, v := range param {
switch vv := v.(type) {
case []interface{}:
fmt.Sprintf(k, "is an array:", vv)
temp, _ := json.Marshal(param)
formatParam = fmt.Sprintf("%sParam = `%s`", interfaceName, fmt.Sprintf("%v", string(temp)))
return
default:
// fmt.Println(k, "is of a type didn't handle")
}
}
}
temp, _ := json.Marshal(param)
formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, fmt.Sprintf("%v", string(temp)))
return
}
POST
postReqParamStr := v.Request.PostData.Text
if v.Request.Method == "POST" {
paramstr = genPostParam(InterfaceName, postReqParamStr)
}
func genPostParam(interfaceName string, postReqParamStr string) (formatParam string) {
// formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, param)
// fmt.Sprintf("%v", string(temp))
postReqParam := make(map[string]interface{}, 1)
if len(postReqParamStr) > 0 {
// 判断第一个字符是否为{}, 做传递数据为数组[]的兼容
if []rune(postReqParamStr)[0] == '{' {
var x interface{}
err := json.Unmarshal([]byte(postReqParamStr), &x)
if err != nil {
fmt.Println("err", err)
}
post