极狐GitLab自动化测试指南02——接口测试
关于JIHULAB 101
为了让极狐GitLab SaaS 的内容丰富起来,同时让更多的小伙伴和极狐GitLab开发者社区一起成长。极狐GitLab开发者社区打造了关于极狐GitLab SaaS用户内容创作成长类项目,即「极狐GitLab创作营 JIHULAB 101」。活动已经在上周五(3 月 18 日),极狐成立一周年之际正式官宣。欢迎各位有优质内容输出的小伙伴参与进来,有大奖在等着你们!
1 理论篇
01
什么是接口测试
WIKI百科对于接口测试的解释是:
接口测试是软件测试的一种,它包括两种测试类型:狭义上指的是直接针对应用程序接口(下面使用缩写API指代,其中文简称为接口)的功能进行的测试;广义上指集成测试中,通过调用API测试整体的功能完成度、可靠性、安全性与性能等指标。
接口测试根据其测试目的不同,来决定测试人员和测试方法:
测试代码内部的接口功能:方法与方法之间,模块与模块之间的交互,是程序内部的接口。如“用户登录”需调用“校验用户身份”和“获取用户信息”方法。更偏向于单元测试,属于白盒测试,大部分由开发人员自测,具体可参考《极狐GitLab自动化测试指南04——单元测试》。
测试系统对外的接口功能:系统与系统之间,或系统与用户之间通过网络数据的传递进行交互。如输入“用户名”和“密码”,进行“用户登录”,成功后返回“用户信息”,失败后返回“登录失败信息”。即通过输入各种参数,来观察输出参数是否符合预期。更偏向于测试这个接口的功能和边界,属于黑盒测试也可以说是灰盒测试,大部分由接口调用方的开发人员进行调试,由测试人员进行测试。本篇主要聚焦该部分内容。
测试系统整体的功能:通过按照一定的业务逻辑调用一组接口,测试一个或多个完整的业务功能。如调用“用户登录”、“添加商品到购物车”等接口,模拟用户在线购物的过程,测试“下单”功能是否正常。更偏向于集成测试,属于黑盒测试,一般由测试工程师进行测试。本篇主要聚焦该部分内容。
测试系统整体的性能:在调用接口实现了测试系统整体功能的基础上,做一些并发请求和指标监控,就可以模拟批量用户同时访问系统的场景,实现对系统性能的测试。属于性能测试,一般由测试工程师进行测试。具体可参考《极狐GitLab自动化测试指南03——性能测试》。
测试系统接口的安全:通过随机参数进行请求,或模拟黑客进行安全攻击等方式对接口进行测试,来检测接口是否安全可靠。属于安全测试,可由安全工程师或测试工程师进行。属于更高层次的测试,本篇不讨论安全方面内容。
02
为什么要做接口测试
接口测试的大前提是要实现前后端分离,但国内目前还有很多企业是靠全干(栈)工程师写的前后端一体化的程序。倒不是说这种程序和系统不能做接口测试,而是因为这种一体化的程序前端后台都是同一个开发人员开发,往往功能耦合比较严重,修改起来牵一发而动全身。此外由于都是自己开发,一般也很少写接口文档,甚至不需要接口文档。所以意义和价值较低,实践起来也比较困难。
接口测试的意义和价值包括但不限于:
越底层发现的Bug,修复成本越低。
不依赖前端页面,可以发现很多在页面上操作发现不了的Bug。
检查系统的安全性、稳定性。如前端通过JS做的校验,后端从接口层面也要实现校验,毕竟前端的校验非常容易绕过。
可通过可视化工具或通过编码的方式进行测试,对测试人员来说容易上手。
容易实现自动化,且相对比较稳定,可用于性能测试和回归测试,能提高测试效率,降低人力成本。
接口测试一般依靠接口文档编写,并通过定期测试,持续验证和更新接口文档,避免文档和接口本身失效或异常。
总体来说,接口测试门槛较低、投入较少、持续性收益较大,且容易实现自动化,所以推广和落地自动化测试,可以先以接口测试作为切入点。
03
如何做接口测试
接口的重要性体现在它是连接多个系统、组织、流程的节点,所以接口测试涉及的人、工具、流程较多,我仅根据个人经验给出一些参考建议,仅覆盖对某个接口的功能测试和对业务功能的集成测试。
人:
后台开发
前端开发
测试人员
对于上述后台和前端开发人员的相关工作,无需赘述,基本上都是这么做的,属于分内之事。而对于测试人员来说,还是再强调一下人员配比和分工的问题。可参考以下方式,具体以实际工作和个人能力调整:
按职能划分:区分接口测试和UI测试,负责接口测试的人员仅做接口层面相关的功能、性能测试。如果团队目前的测试人员主要是通过手工在UI层面进行集成测试,且工作基本饱和。建议新增测试人员,专门负责接口测试。
按模块划分:一组测试人员负责一个或多个系统模块的测试。这组测试人员要负责对应模块的各方面测试,一般职能上有交叉,整体上互补。比如一个测试人员主要做接口测试,兼做部分手工的UI测试;另一个测试人员主要做UI的手工测试或自动化测试。
工具:
接口测试常见的工具如下:
接口文档:Swagger、Yapi
接口测试:Postman、Postwoman、基于Python开发
Mock:EasyMock、Mockito、mock.js
性能测试:Jmeter、Locust
整体来看,较多的工具会增加管理维护成本,此外不同工具由于数据割裂,也会导致重复工作,降低协同效率。比如接口在开发阶段比较容易发生变化,但接口与接口文档的不同步或者变更没有及时通知到相关人员,就会影响整个工作。再比如常见的工作流程是开发人员在接口文档工具中定义接口,在代码开发中需要再次定义接口,在调试阶段中需要用调试工具继续定义接口,最后到测试阶段测试人员还要再次定义。
所以近期也出现了一款比较火的API“测试”工具Apifox。官方介绍如下:
Apifox 是接口管理、开发、测试全流程集成工具,定位 Postman + Swagger + Mock + JMeter。通过一套系统、一份数据,解决多个系统之间的数据同步问题。只要定义好接口文档,接口调试、数据 Mock、接口测试就可以直接使用,无需再次定义;接口文档和接口开发调试使用同一个工具,接口调试完成后即可保证和接口文档定义完全一致。高效、及时、准确。
仅就接口测试工具本身来说,最常见的还是老牌工具Postman,也有不少公司会基于Python等开发语言编写自己的接口自动化测试框架。但对于接口测试起步阶段的企业来说,建议使用可视化的工具来降低门槛。
流程:
正如上文提到的,接口测试既可以围绕一个接口测试其边边界和功能, 也可以围绕多个接口的编排测试其业务功能。所以侧重点不同,工作的流程也不同。
对于起步阶段的企业或团队,可以只聚焦测试某个接口的边界和功能。而对于已经建立起接口自动化测试能力的企业或团队,可以将接口测试的范围扩大至业务功能测试甚至后面将要讲到的性能测试。以下工作流程仅供参考:
准备阶段
开发阶段
测试阶段
数据:
接口测试的本质还是测试接口输入、输出的数据是否准确,所以测试数据的准备和维护非常重要。假设要测试“注册账号”的接口,输入数据是:
username: zhangsan
password: zhangsan@123
那么这个数据在完成“注册账号”接口测试后,就无法再次测试“注册账号”接口了,因为理论上一个系统不允许存在同用户名的账号,除非使用其他测试数据,或删除这个测试数据。测试数据无法重复使用,意味着无法实现自动化测试。所以如何创建或者删除测试数据,常见的一般有两种方式:
编写脚本:通过脚本调用接口,实现数据的批量创建或者删除。
操作数据库:通过SQL直接操作数据库,实现数据的批量创建或者删除。
两种方式各有优劣,可以根据实际情况选择,个人倾向后者。不论采用哪种方案,完成一轮测试后,一定需要删除相关的测试数据,才能满足自动化接口测试的条件。
2 实践篇
2.1 极狐GitLab集成Apifox
2.1.1
创建文档项目
假设我们要开发一个唱片管理系统,需要实现以下功能:
发布唱片信息(ID、唱片名、艺术家、价格)
获取唱片列表(ID、唱片名、艺术家、价格)
获取指定ID的唱片
基于上述需求,先使用Apifox建立接口文档。Apifox官方提供了详细的介绍和操作文档,详见:快速上手 | Apifox 使用文档[1]。目前Apifox有SaaS版和私有化部署版,其中SaaS版免费使用,而是私有化部署需要付费使用。Apifox的客户端又分为桌面版和Web版,以桌面版为例:
1. 创建一个名为album的项目。
2. 创建数据模型。如下图,使用了Apifox自带的“从JSON/XML智能识别”功能和“智能Mock”功能。详见:
数据结构 / 数据模型 | Apifox 使用文档[2]
Mock 功能说明 | Apifox 使用文档[3]
3. 创建接口文档。如下图,引用上一步创建的数据模型,就可以快速生成返回示例,可用于运行Mock服务,运行通过后可以保存为接口用例。详见:
接口设计 (接口文档) | Apifox 使用文档[4]
接口调试 / 接口用例 | Apifox 使用文档[5]
4. 创建好接口文档后,开发人员就可以根据文档中的示例数据或者Mock数据进行开发。而测试人员可以进一步完善接口用例,比如设置一些测试数据以及设置断言,并基于接口用例编写测试用例。其中接口用例指的主要是对接口的功能和边界进行测试,而测试用例主要是对一组接口实现的业务功能进行测试。详见:
断言 (测试请求结果) | Apifox 使用文档[6]
测试用例 | Apifox 使用文档[7]
2.1.2
开发服务端程序
以Golang为例,开发一个服务端程序api-demo-golang,实现接口文档的相关功能:
使用极狐GitLab托管源代码,服务默认端口9080,参考示例main.go如下:
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// album represents data about a record album.
type album struct {
ID int `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// albums slice to seed record album data.
var albums = []album{
{ID: 1, Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: 2, Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: 3, Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run(":9080")
}
// getAlbums responds with the list of all albums as JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
// postAlbums adds an album from JSON received in the request body.
func postAlbums(c *gin.Context) {
var newAlbum album
// Call BindJSON to bind the received JSON to
// newAlbum.
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// Add the new album to the slice.
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
// getAlbumByID locates the album whose ID value matches the id
// parameter sent by the client, then returns that album as a response.
func getAlbumByID(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
// Loop through the list of albums, looking for
// an album whose ID value matches the parameter.
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
2. 创建该项目的流水线,实现自动构建,并发布到测试环境。需Docker/K8S类型的GitLab Runner,详见:GitLab Runner Executors | GitLab[8]。参考示例.gitlab-ci.yaml如下:
stages:
- build
- deploy
# 编译构建打包
build-job:
stage: build
image: golang:1.17
script:
- go mod tidy
- go build -o api-demo .
artifacts:
paths:
- api-demo
# 发布到测试环境并运行,可参考 https://docs.gitlab.com/ee/ci/ssh_keys/index.html#ssh-keys-when-using-the-docker-executor
deploy-job:
stage: deploy
image: debian:latest
before_script:
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- scp -P $DEPLOY_PORT ./api-demo $DEPLOY_USER@$DEPLOY_HOST:/home/ubuntu/
- ssh -p $DEPLOY_PORT $DEPLOY_USER@$DEPLOY_HOST "./api-demo >/dev/null 2>&1 &"
待流水线成功运行后,确定测试环境IP:9080可访问后就可以进行后续的测试。
2.1.3
手动执行测试
1. 当后台开发人员将接口服务发布到测试环境后,测试人员就要基于测试环境进行接口测试,而不是继续做Mock。可在Apifox中配置管理不同的环境,并切换不同的环境进行测试。
2. 在测试用例中,选择测试环境然后运行,查看测试报告。若有未通过的用例,则走Bug提报流程或调试调整测试用例;若用例全部通过,则可进行下一步,将这些测试用例通过极狐GitLab CI/CD 进行集成,实现自动化测试。
2.1.4
自动化测试
-
在极狐GitLab中创建名为api-test-golang的项目,用来管理接口的自动化测试用例。
2. 将手动测试通过的测试用例导出为Apifox CLI格式文件,并上传到极狐GitLab的api-test-golang项目中,如果有多个测试用例就有多个文件。
3. 创建api-test-golang项目的CI/CD流水线,详见:Apifox CLI 命令行运行 | Apifox 使用文档[9]。参考示例.gitlab-ci.yaml如下:
stages:
- test
api-test-job:
stage: test
# 执行apifox自动化测试需nodejs环境
image: node:12
# 安装apifox-cli,执行测试用例,输出cli和html格式报告
script:
- npm install -g apifox-cli
- apifox run *.json -r cli,html
# 可自行实现邮件或IM工具发送通知
# 上传报告
artifacts:
paths:
- apifox-reports/*
成功运行流水线后,可进入CI/CD作业(Job),通过日志查看测试报告:
也可下载作业(Job)的制品(Artifacts),或配合极狐GitLab Pages功能,以HTML格式查看测试报告:
4. 利用极狐GitLab跨项目流水线功能,实现api-demo-golang部署到测试环境后自动触发api-test-golang的接口自动化测试。修改api-demo-golang的.gitlab-ci.yaml:
stages:
- build
- deploy
- test # 增加test阶段
# 编译构建打包
build-job:
stage: build
image: golang:1.17
script:
- go mod tidy
- go build -o api-demo .
artifacts:
paths:
- api-demo
# 发布到测试环境并运行,可参考 https://docs.gitlab.com/ee/ci/ssh_keys/index.html#ssh-keys-when-using-the-docker-executor
deploy-job:
stage: deploy
image: debian:latest
before_script:
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- scp -P $DEPLOY_PORT ./api-demo $DEPLOY_USER@$DEPLOY_HOST:/home/ubuntu/
- ssh -p $DEPLOY_PORT $DEPLOY_USER@$DEPLOY_HOST "./api-demo >/dev/null 2>&1 &"
# 增加跨项目流水线,自动触发api-test-golang项目的流水线
api-test-job:
stage: test
trigger:
project: <群组>/<子群组>/api-test-golang
strategy: depend
运行api-demo-golang的流水线,可以看到下游流水线api-test-golang被成功触发。如果使用极狐GitLab专业版,还可以在上游流水线中直接查看下游流水线的构建状态和日志,如下图所示:
至此,就可以实现基于极狐GitLab和Apifox的自动化接口测试。持续维护api-test-golang项目,每次服务端程序发布后,就可以自动的进行接口全量回归测试。
2.2 极狐GitLab集成 Postman
网络上有关Postman做接口测试的教程实在太多了,作为老牌工具功能也很强大,也支持团队协作、文档管理。但在实际工作中很多企业的测试人员只是拿Postman来做接口测试,不做接口文档的管理和协作。所以这里也仅介绍如何将Postman集成到极狐GitLab CI/CD做自动化接口测试,整体的工作流程可以参考上文。
在极狐GitLab中创建一个项目,用来管理Postman的接口自动化测试用例。
将Postman的测试用例导出为json格式文件,并上传到上一步创建的极狐GitLab项目中,如果有多个测试用例就有多个文件。
3. 创建该项目的CI/CD流水线,参考示例.gitlab-ci.yaml如下:
stages:
- test
api-test-job:
stage: test
# 执行postman自动化测试需newman工具
image: postman/newman:latest
# 安装html报告插件,执行测试用例,输出cli和html格式报告
script:
- npm install -g newman-reporter-html
- newman run *.json --reporters cli,html --reporter-html-export report.html
# 可自行实现邮件或IM工具发送通知
# 上传报告
artifacts:
paths:
- report.html
同样可以在流水线任务的日志中查看报告,也可以查看HTML格式的报告。依然可以参考前文,设置跨项目流水线,实现接口服务发布后自动进行接口测试。
参考链接
https://www.apifox.cn/help/app/getting-started/
https://www.apifox.cn/help/app/api-manage/api-schema/#%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B
https://www.apifox.cn/help/app/mock/
https://www.apifox.cn/help/app/api-manage/api-design/
https://www.apifox.cn/help/app/api-manage/api-case/
https://www.apifox.cn/help/app/scripts/examples/tests/
https://www.apifox.cn/help/app/test-manage/test-case/
https://docs.gitlab.com/runner/executors/
https://www.apifox.cn/help/cli/
极狐(GitLab) 以“核心开放”为原则,面向中国市场,提供开箱即用的开放式一体化安全DevOps平台——极狐GitLab。通过业界领先的优先级管理、安全、风险和合规性功能,实现产品、开发、QA、安全和运维团队间的高效协同,加速和优化企业软件开发生命周期。极狐GitLab由极狐信息技术(湖北)有限公司在国内独立运营,提供中国本地化功能和支持服务。极狐(GitLab)公司以“中外合资3.0”模式创立,公司投资方包括GitLab Inc.、红杉宽带跨境数字产业基金,以及高成资本。
点击“阅读原文”,参与 JIHULAB 101,领取你的专属卡片吧!