使用Go语言开发自动化API测试工具详解

 更新时间:2024年03月08日 10:58:30   作者:程序设计实验室  
这篇文章主要为大家详细介绍了如何使用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 方法

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 方法

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, ZerologZap, 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 比较好用。

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

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 可以配置多个输出,也可以设置按日志大小分割文件,还可以对接其他日志收集平台啥的,基本做到了现代日志组件的水平了…

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

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 安装

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 挺多,上手就直接用了

感觉还行。

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 来模拟

func If[T any](condition bool, trueVal, falseVal T) T {
  if condition {
    return trueVal
  }
  return falseVal
}

使用的时候就

result := If[string](report.IsPassed, "成功", "没通过")

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

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

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

数组排序

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

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

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

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

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

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

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

testReports.Sort((a, b) => a - b);

小结

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

参考资料

uber的Go日志库zap使用详解 -Golang日志操作库zap的使用详解

以上就是使用Go语言开发自动化API测试工具详解的详细内容,更多关于Go自动化API测试工具的资料请关注脚本之家其它相关文章!

相关文章

  • golang切片内存应用技巧详解

    golang切片内存应用技巧详解

    这篇文章主要介绍了golang切片内存应用技巧详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • 在go中使用omitempty的代码实例

    在go中使用omitempty的代码实例

    今天小编就为大家分享一篇关于在go中使用omitempty的代码实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • 详解如何利用GORM实现MySQL事务

    详解如何利用GORM实现MySQL事务

    为了确保数据一致性,在项目中会经常用到事务处理,对于MySQL事务相信大家应该都不陌生。这篇文章主要总结一下在Go语言中Gorm是如何实现事务的;感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助
    2022-09-09
  • Go语言context上下文管理的使用

    Go语言context上下文管理的使用

    本文主要介绍了Go语言context上下文管理的使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Go语言中的switch用法实例分析

    Go语言中的switch用法实例分析

    这篇文章主要介绍了Go语言中的switch用法,实例分析了switch的功能及使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • 如何使用Golang发送Get和Post请求

    如何使用Golang发送Get和Post请求

    这篇文章主要给大家介绍了关于如何使用Golang发送Get和Post请求的相关资料,Go语言(Golang)的标准库提供了处理HTTP请求的功能,这使得将Go用于web应用程序变得非常容易,需要的朋友可以参考下
    2023-06-06
  • 浅析Go语言中的map数据结构是如何实现的

    浅析Go语言中的map数据结构是如何实现的

    在 Go 中,map 是一种用于存储键值对的数据结构,它提供了一种快速查找和访问数据的方式,下面我们就来看看Go语言中是如何实现map数据结构的吧
    2024-03-03
  • Go语言轻量级高性能嵌入式规则引擎RuleGo使用详解

    Go语言轻量级高性能嵌入式规则引擎RuleGo使用详解

    这篇文章主要为大家介绍了Go语言轻量级高性能嵌入式规则引擎RuleGo使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Go项目与Docker结合实现高效部署深入探究

    Go项目与Docker结合实现高效部署深入探究

    在现代软件开发中,使用Docker部署应用程序已经成为一种标准实践,本文将深入探讨如何将Go项目与Docker结合,实现高效、可靠的部署过程,通过详细的步骤和丰富的示例,你将能够迅速掌握这一流程
    2023-12-12
  • Go语言MD5加密用法实例

    Go语言MD5加密用法实例

    这篇文章主要介绍了Go语言MD5加密用法,实例分析了Go语言MD5加密的使用技巧,需要的朋友可以参考下
    2015-03-03

最新评论