使用go语言开发自动化API测试工具

前言

上一篇文章说到我还开发了一个独立的自动测试工具,可以根据 OpenAPI 的文档来测试,并且在测试完成后输出测试报告,报告内容包括每个接口是否测试通过和响应时间等。

这个工具我使用了 go 语言开发,主要是考虑到了 go 语言可以傻瓜式的实现交叉编译,生成的可执行文件直接上传到服务器就可以执行,非常方便。

PS: go 语言写起来是真的折磨!感觉语法有很多别扭的地方,不过 build 的时候实在太爽了,根本无法拒绝😂

为了避免篇幅太长,本文先介绍用到的组件,详细实现以及解析 OpenAPI 文档生成测试配置的部分后续的文章再介绍。

网络请求

标准库中的 net/http 包提供了发送 HTTP 请求的功能,拿到数据之后,使用 json.Unmarshal 函数解析 JSON 数据。这个包相对比较低级,对于简单的网络请求,够用,不过我还是想选择更好用的组件。

Resty 是一个简单而强大的 Go HTTP 客户端,具有链式 API,可以轻松地发送 HTTP 请求并处理 JSON 数据。它提供了丰富的功能,包括自动重试、超时设置、请求和响应日志等。您可以使用 Resty 来发送 GET、POST、PUT、DELETE 等各种类型的请求,并且它能够自动将响应的 JSON 数据解析为 Go 结构体。

现在出了 v2 版本,支持 HTTP/2、WebSocket、Cookie 操作,并提供了更加简洁和易用的 API 。

项目地址: https://github.com/go-resty/resty

使用起来还行

GET 方法

go 复制代码
import 	"github.com/go-resty/resty/v2"

req := c.RestyClient.R().SetHeader("Authorization", "token "+c.AuthToken)

req.SetQueryParams(map[string]string{
  "year":  "2024",
})
resp, err = req.Get("path")

POST 方法

go 复制代码
req.SetBody(map[string]string{
  "year":  "2024",
})
resp, err = req.Get("path")

SetBody 的参数是 interface{} 类型,可以传入的类型比较丰富,我这里还是跟 GET 一样传了字典,实际上应该传 struct 比较多一些吧。

日志组件

我之前用的是 go 语言内置的 log ,但似乎功能很少,也没有日志等级啥的,这能叫日志库吗......

接着我找到了在 GitHub 上 star 很多的 logrus 库,不过感觉这是一个比较古老的库了,不太好用,formatter 也没找到好用的,看项目主页的介绍发现这个库已经进入退休状态...

它让我 Check out, for example, Zerolog, Zap, and Apex.

Logrus is in maintenance-mode. We will not be introducing new features. It's simply too hard to do in a way that won't break many people's projects, which is the last thing you want from your Logging library (again...).

项目地址: https://github.com/sirupsen/logrus

所以,最终还是用了 uber 的日志库 go.uber.org/zap

logrus 使用 & 配置

虽然后面换了 zap ,还是记录一下关于 logrus 的使用。

项目主页上列举的几个第三方 formatter 我基本都试用了,就这个 nested-logrus-formatter 比较好用。

以下配置实现了同时输出日志到控制台和文件。

go 复制代码
import (
  nested "github.com/antonfisher/nested-logrus-formatter"
  "github.com/sirupsen/logrus"
  "os"
)

func initLogger() *os.File {
  logger.SetLevel(logrus.DebugLevel)
  logger.SetReportCaller(true)
  logger.SetFormatter(&nested.Formatter{})

  // 创建一个文件作为日志输出
  file, err := os.OpenFile("logfile.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
  if err != nil {
    logger.Fatalf("无法打开日志文件: %v", err)
  }

  // 创建一个多写入器,将日志同时输出到控制台和文件
  mw := io.MultiWriter(os.Stdout, file)

  // 添加 Hook 到 Logger 中
  logger.Out = mw

  return file
}

func main() {
  file := initLogger()
  defer func(file *os.File) {
    err := file.Close()
    if err != nil {
      fmt.Println(err)
    }
  }(file)
}

zap 使用 & 配置

zap 比起 logrus 好用多了,开箱即用,搭配 zapcore 可以配置多个输出,也可以设置按日志大小分割文件,还可以对接其他日志收集平台啥的,基本做到了现代日志组件的水平了...

一样是实现了同时输出日志到控制台和文件。

go 复制代码
import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
)

func buildLogger() *zap.SugaredLogger {
  config := zap.NewProductionEncoderConfig()
  config.EncodeTime = zapcore.ISO8601TimeEncoder
  consoleEncoder := zapcore.NewConsoleEncoder(config)
  fileEncoder := zapcore.NewJSONEncoder(config)
  logFile, _ := os.OpenFile("./log-test-zap.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 06666)

  tee := zapcore.NewTee(
    zapcore.NewCore(fileEncoder, zapcore.AddSync(logFile), zap.DebugLevel),
    zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), zap.DebugLevel),
  )
  var zapLogging = zap.New(
    tee,
    zap.AddCaller(),
    zap.AddStacktrace(zapcore.ErrorLevel),
  )

  var logger = zapLogging.Sugar()
  return logger
}

func main() {
  logger := buildLogger()
  defer logger.Sync()
}

swagger 组件

这里我试着用了一下 go swagger

项目地址: https://github.com/go-swagger/go-swagger

这个可以使用 scoop 安装

bash 复制代码
scoop install go-swagger

用着一般,没有 swagger code generator 好用。

就没继续探索下去了。

PS: Jetbrains 系的 IDE 里有几成 swagger code generator 工具,功能非常强大,可以生成各种代码。

不过我还是自己实现了OpenAPI 文档解析(比较灵活),所以暂时还没用上这个强大的工具。

Excel 导出

Excel 操作我用的是这个 github.com/xuri/excelize/v2

一开始没注意,后面发现这个居然是奇安信开源的...... 然后看了 qax-os 这个 group ,发现开源的几个项目都是跟安全无关的,不务正业啊老哥!😂

没有对比其他的,看着 star 挺多,上手就直接用了

感觉还行。

go 复制代码
func exportTestReportsToExcel(testReports []*tester.Report, filename string) error {
  // 创建一个新的 Excel 文件
  f := excelize.NewFile()

  // 创建一个名为 "测试报告" 的工作表
  index, err := f.NewSheet("测试报告")
  if err != nil {
    return err
  }

  // 设置工作表列名
  f.SetCellValue("测试报告", "A1", "接口名称")
  f.SetCellValue("测试报告", "B1", "接口路径")
  f.SetCellValue("测试报告", "C1", "测试是否通过")
  f.SetCellValue("测试报告", "D1", "耗时(秒)")

  // 遍历测试报告并在工作表中写入数据
  for i, report := range testReports {
    row := i + 2
    f.SetCellValue("测试报告", fmt.Sprintf("A%d", row), report.ApiName)
    f.SetCellValue("测试报告", fmt.Sprintf("B%d", row), report.ApiPath)
    f.SetCellValue("测试报告", fmt.Sprintf("C%d", row), func() string {
      if report.IsPassed {
        return "是"
      }
      return "否"
    }())
    f.SetCellValue("测试报告", fmt.Sprintf("D%d", row), report.Elapsed.Seconds())
  }

  // 设置活动工作表
  f.SetActiveSheet(index)

  // 将 Excel 文件保存到磁盘
  err = f.SaveAs(filename)
  if err != nil {
    return err
  }

  return nil
}

吐槽

三元表达式

我很想吐槽 go 为啥没有三元表达式,用匿名函数真的好繁琐啊!!

据说是因为觉得三元表达式可以写出很多让人看不懂的骚代码,所以 go 不打算支持,因噎废食啊😂

不过这难不倒我,可以写个函数来模拟,而且现在 go 似乎更新了泛型的功能,不用再拿 interface 来模拟

go 复制代码
func If[T any](condition bool, trueVal, falseVal T) T {
  if condition {
    return trueVal
  }
  return falseVal
}

使用的时候就

go 复制代码
result := If[string](report.IsPassed, "成功", "没通过")

支持类型推导,所以 [string] 也可以省略了。

这样前面导出 Excel 的代码里的匿名函数就可以改成这样,简洁多了!

go 复制代码
f.SetCellValue("测试报告", fmt.Sprintf("C%d", row), If(report.IsPassed, "是", "否"))

数组排序

本来也不算什么吐槽,属于是挑刺了,go 的排序没那么好用,但也不难用。

用匿名函数可以实现按字段排序,这倒是和 C 语言里用函数指针大同小异,不愧是带 gc 的 C 语言

测试报告的数据结构是这样

go 复制代码
// Report 测试报告
type Report struct {
  ApiName  string
  ApiPath  string
  IsPassed bool
  Elapsed  time.Duration
  Response *ApiResponse
}

我想对 []*Report 数组排序,可以用 sort.Slice 方法

go 复制代码
// 按照 Elapsed 属性排序,从大到小
sort.Slice(testReports, func(i, j int) bool {
  return testReports[i].Elapsed > testReports[j].Elapsed
})

相比之下还是 cs 的 Linq 舒服啊

c# 复制代码
testReports.Sort((a, b) => a - b);

小结

就这样吧,很简单的一个小工具,因为还处在 go 的小白阶段,每用一个新的库都会记录一下。

参考资料

相关推荐
蒙娜丽宁2 天前
Go语言错误处理详解
ios·golang·go·xcode·go1.19
qq_172805593 天前
GO Govaluate
开发语言·后端·golang·go
littleschemer3 天前
Go缓存系统
缓存·go·cache·bigcache
程序者王大川4 天前
【GO开发】MacOS上搭建GO的基础环境-Hello World
开发语言·后端·macos·golang·go
Grassto4 天前
Gitlab 中几种不同的认证机制(Access Tokens,SSH Keys,Deploy Tokens,Deploy Keys)
go·ssh·gitlab·ci
高兴的才哥5 天前
kubevpn 教程
kubernetes·go·开发工具·telepresence·bridge to k8s
少林码僧6 天前
sqlx1.3.4版本的问题
go
蒙娜丽宁6 天前
Go语言结构体和元组全面解析
开发语言·后端·golang·go
蒙娜丽宁6 天前
深入解析Go语言的类型方法、接口与反射
java·开发语言·golang·go
三里清风_6 天前
Docker概述
运维·docker·容器·go