写点什么

一个 Golang 项目的测试实践全记录

  • 2019-09-17
  • 本文字数:4130 字

    阅读完需:约 14 分钟

一个 Golang 项目的测试实践全记录

最近有一个项目,链路涉及了 4 个服务。最核心的是一个配时服务。要如何对这个项目进行测试,保证输出质量,是最近思考和实践的重点。这篇就说下最近这个实践的过程总结。

测试金字塔

按照 Mike Cohn 提出的“测试金字塔”概念,测试分为 4 个层次:



最下面是单元测试,单元测试对代码进行测试。再而上是集成测试,它对一个服务的接口进行测试。继而是端到端的测试,我也称他为链路测试,它负责从一个链路的入口输入测试用例,验证输出的系统的结果。再上一层是我们最常用的 UI 测试,就是测试人员在 UI 界面上根据功能进行点击测试。

单元测试

对于一个 Golang 写的服务,单元测试已经是很方便了。我们在写一个文件,函数的时候,可以直接在需要单元测试的文件旁边增加一个_test.go 的文件。而后直接使用 go test 直接跑测试用例就可以了。


一般单元测试,最直接的衡量标准就是代码覆盖率。单元测试一般测试的对象是一个函数,一个类。这个部分已经有很多实践例子了,就补在赘述。

集成测试

思考和需求

对于一个服务,会提供多个接口,那么,测试这些接口的表现就是集成测试最重要的目标了。只有通过了集成测试,我们的这个服务才算是有保障。


手头这个配时项目,对外提供的是一系列 HTTP 服务,基本上代码是以 MVC 的形式架构的。在思考对它的集成测试过程中,我希望最终能做到下面几点:


首先,我希望我手上这个配时服务的集成测试是自动化的。最理想的情况下,我能调用一个命令,直接将所有 case 都跑一遍。


其次,衡量集成测试的达标指标。这个纠结过一段时间,是否需要有衡量指标呢?还是直接所有 case 通过就行?我们的服务,输入比较复杂,并不是简单的 1-2 个参数,是一个比较复杂的 json。那么这个 json 的构造有各种各样的。需要实现写一些 case,但是怎么保证我的这些 case 是不是有漏的呢?这里还是需要有个衡量指标的,最终我还是选择用代码覆盖率来衡量我的测试达标情况,但是这个代码覆盖率在 MVC 中,我并不强制要求所有层的所有代码都要覆盖住,主要是针对 Controller 层的代码。controller 层主要是负责流程控制的,需要保证所有流程分支都能走到。


然后,我希望集成测试中有完善的测试概念,主要是 TestCase, TestSuite,这里参考了 JUnit 的一些概念。TestCase 是一个测试用例,它提供测试用例启动和关闭时候的注入函数,TestSuite 是一个测试套件,代表的是一系列类似的测试用例集合,它也带测试套件启动和关闭时候的注入函数。


最后,可视化需求。我希望这个测试结果很友好,能有一个可视化的测试界面,我能很方便知道哪个测试套件,哪个测试用例中的哪个断言失败了。

集成测试实践

Golang 只有 test.go 的测试,其中的每个 TestXXX 相当于是 TestCase 的概念,也没有提供测试 case 启动,关闭执行的注入函数,也没有 TestSuite 的概念。首先我需要使用 Golang 的 test 搭建一个测试架子。


集成测试和单元测试不一样,它不属于某个文件,集成测试可能涉及到多个文件中多个接口的测试,所以它需要有一个单独的文件夹。它的目录结构我是这么设计的:



suites


存放测试套件


suites/xxx


这里存放测试套件,测试套件文件夹需要包含下列文件:


before.go 存放有


  • SetUp() 函数,这个函数在 Suite 运行之前会运行

  • Before() 函数,这个函数在所有 Case 运行之前运行


after.go 存放有


  • TearDown() 函数,这个函数在 Suite 运行之后会运行

  • After() 函数,这个函数在 Suite 运行之后运行


run_test.go 文件


这个文件是 testsuite 的入口,代码如下:


package adaptimport"testing"import . "github.com/smartystreets/goconvey/convey"func TestRunSuite(t *testing.T) {SetUp()    defer TearDown()Convey("初始化", t, nil)    runCase(t, NormalCasePEE001)    runCase(t, PENormalCase01)    runCase(t, PENormalCase04)    runCase(t, PENormalCase11)    runCase(t, PENormalCase13)    runCase(t, PENormalCase14)    runCase(t, NormalCasePIE001)    runCase(t, NormalCasePIE002)    runCase(t, NormalCase01)    runCase(t, NormalCase02)    runCase(t, NormalCase07)    runCase(t, NormalCase08)    runCase(t, NormalCasePIN003)    runCase(t, NormalCasePIN005)    runCase(t, NormalCasePIN006)    runCase(t, NormalCasePIN015)}func runCase(t *testing.T, testCase func(*testing.T)) {Before()    defer After()    testCase(t)}

复制代码


envionment


初始化测试环境的工具


当前我这里面存放了初始化环境的配置文件和 db 的建表文件。


report


存放报告的地址


代码覆盖率需要额外跑脚本


在 tester 目录下运行:sh coverage.sh 会在 report 下生成 coverage.out 和 coverage.html,并自动打开浏览器。

引入 Convey

关于可视化的需求。


我引入了 Convey 这个项目,http://goconvey.co/ 。第一次看到这个项目,觉得这个项目的脑洞真大。


下面可了劲的夸一夸这个项目的优点:

断言

首先它提供了基于原装 go test 的断言框架;提供了 Convey 和 So 两个重要的关键字,还提供了 Shouldxxx 等一系列很好用的方法。它的测试用例写下来像是这个样子:


package package_nameimport ("testing"    . "github.com/smartystreets/goconvey/convey"
)


func TestIntegerStuff(t *testing.T) {Convey("Given some integer with a starting value", t, func() { x := 1 Convey("When the integer is incremented", func() { x++ Convey("The value should be greater by one", func() { So(x, ShouldEqual, 2) }) }) })}
复制代码


很清晰明了,并且超赞的是很多参数都使用函数封装起来了,go 中的 := 和 = 的问题能很好避免了。并且不要再绞尽脑汁思考 tmp1,tmp2 这种参数命名了。(因为都已经分散到 Convey 语句的 func 中了)

Web 界面

其次,它提供了一个很赞的 Web 平台,这个 web 平台有几个点我非常喜欢。首先它有一个 case 编辑器。



什么叫好的测试用例实践? 我认为这个编辑器完全体现出来了。写一个完整的 case 先考虑流程和断言,生成代码框架,然后我们再去代码框架中填写具体的逻辑。这种实践步骤很好解决了之前写测试用例思想偷懒的问题,特别是断言,基本不会由于偷懒而少写。


其次它提供很赞的测试用例结果显示页面:



很赞吧,哪个 case 错误,哪个断言问题,都很清楚显示出来。


还有,goconvey 能监控你运行测试用例的目录,当目录中有任何文件改动的时候,都会重新跑测试用例,并且提供提醒



这个真是太方便了,可以在每次保存的时候,都知道当前写的 case 是否有问题,能直接提高测试用例编写的效率。

TestSuite 初始化

Web 服务测试的环境是个很大问题。特别是 DB 依赖,这里不同的人有不同的做法。有使用 model mock 的,有使用 db 的。这里我的经验是:集成测试尽量使用真是 DB,但是这个 DB 应该是私有的,不应该是多个人共用一个 DB。


所以我的做法,把需要初始化的 DB 结构使用 sql 文件导出,放在目录中。这样,每个人想要跑这一套测试用例,只需要搭建一个 mysql 数据库,倒入 sql 文件,就可以搭建好数据库环境了。其他的初始化数据等都在 TestSuite 初始化的 SetUp 函数中调用。


关于保存测试数据环境,我这里有个小贴士,在 SetUp 函数中实现 清空数据库+初始化数据库 ,在 TearDown 函数中不做任何事情。这样如果你要单独运行某个 TestSuite,能保持最后的测试数据环境,有助于我们进行测试数据环境测试。

TestCase 编写

在集成测试环境中,TestCase 编写调用 HTTP 请求就是使用正常的 httptest 包,其使用方式没有什么特别的。

代码覆盖率

goconvey 有个小问题,测试覆盖率是根据运行 goconvey 的目录计算的,不能额外设置,但是 go test 是提供的。所以代码覆盖率我还额外写了一个 shell 脚本。


#!/bin/bash


go test -coverpkg xxx/controllers/... -coverprofile=report/coverage.out ./...
go tool cover -html=report/coverage.out -o report/coverage.html
open report/coverage.html
复制代码


主要就是使用 converpkg 参数,把代码覆盖率限制在 controller 层。


继承测试总结

这套搭建实践下来,对接口的代码测试有底很多了,也测试出不少 controller 层面的 bug。

端到端测试

这个是测试金字塔的第二层了。


关于端到端的测试,我的理解就是全链路测试。从整个项目角度来看,它属于一个架构的层次了,需要对每个服务有一定的改造和设计。这个测试需要保证的是整个链路流转是按照预期的。


比如我的项目的链路通过了 4 个服务,一个请求可能在多个服务之间进行链路调用。但是这个项目特别的是,这些服务并不都是一个语言的。如何进行测试呢?


理想的端到端测试我的设想是这样的,测试人员通过 postman 调用最上游的服务,构造不同的请求参数和 case,有的 case 其实可能无法通到最下游,那么就需要有一个全链路日志监控系统,在这个系统可以看到这个请求在各个服务中的流转情况。全链路日志监控系统定义了一套 tag 和一个 traceid,要求所有服务在打日志的时候带上这个 traceid,和当前步骤的 tag,日志监控系统根据这些日志,在页面上能很好反馈出这个链路。


然后测试人员每个 case,就根据返回的 traceid,去日志中查找,并且确认链路中的 tag 是否都全齐。


关于如何在各个服务中传递 traceid,这个很多微服务监控的项目中都已经说过了,我也是一样的做法,在 http 的 header 头中增加这个 traceId。


关于打日志的地方,其实有很多地方都可以打日志,但是我只建议在失败的地方+请求的地方打上 tag 日志,并且是由调用方进行 tag 日志记录,这样主要是能把请求和返回都记录,方便调试,查错等问题。

UI 测试

这个目前还是让测试人员手动进行点击。这种方式看起来确实比较 low,但是貌似也是目前大部分互联网公司的测试方法了。

总结

这几周主要是在集成测试方面做了一些实践,有一些想法和思路,所以拿出来进行了分享,肯定还有很多不成熟的地方没有考虑到,欢迎评论留言讨论。


测试是一个费时费力的工作,大多数情况下,业务的迭代速度估计都不允许做很详细的测试。但是对于复杂,重要的业务,强烈建议这四层的测试都能做到,这样代码上线才能有所底气。


本文转载自公众号滴滴技术(ID:didi_tech)。


原文链接:


https://mp.weixin.qq.com/s/5k7zDzMmGNHFF6iUURJRZQ


2019-09-17 17:242581

评论

发布
暂无评论
发现更多内容

Blackbox AI:你的智能编程伙伴

Yan-英杰

人工智能 编译器 大模型

蓝易云 - Weblogic下启用Gzip压缩

百度搜索:蓝易云

云计算 运维 云服务器 weblogic gzip

【第七在线】智能推算辅助选品组货 驱动零售品牌业绩增长

第七在线

虚拟现实和元宇宙技术应用的差异

3DCAT实时渲染

实时云渲染 元宇宙解决方案 VR虚拟现实

蓝易云 - vue组件开发中props和data之间的区别

百度搜索:蓝易云

运维 Vue Data 云服务器 props

3CX的具体介绍

cts喜友科技

通信 通讯 云通讯

如何卸载Maxon产品?红巨星系列插件如何彻底清除

Rose

Parallels Desktop虚拟机 联网失败和不能连接USB设备解决方法

Rose

基于 MelosBoom ,捕获 DePIN 赛道发展红利

股市老人

蓝易云 - Oracle drop删除表如何恢复

百度搜索:蓝易云

数据库 oracle 云计算 云服务器 高防服务器

喜报!极限科技再获国家发明专利:《一种超大规模分布式集群架构的数据处理方法》,引领大数据处理技术创新

极限实验室

大数据 数据处理 国家专利

刘鸿良的励志传奇与数字经济领航

科技热闻

FileMaker Pro 18 Advance Mac版 数据库软件

Rose

苏州1U,2U服务器托管选择哪个机房性价比高?如何托管?

苏州服务器托管

数据中心 IDC 服务器托管

从入门到精通,SnailSVN Pro——您的专业版SVN解决方案

Rose

客户在哪儿AI的ToB获客服务和AI外呼机器人的有何不同

客户在哪儿AI

ToB营销 ToB获客 ToB增长 大客户营销

项目进度跟踪系统:选型全攻略

爱吃小舅的鱼

项目进度 项目进度管理

蓝易云 - 企业云服务器运维需要了解哪些事?

百度搜索:蓝易云

云计算 运维 云服务器 高防服务器 企业云服务器

Python函数式编程入门窥探

我再BUG界嘎嘎乱杀

Python 编程 后端 函数 开发语言

这份Excel+Python飞速搞定数据分析手册,简直可以让Excel飞起来

我再BUG界嘎嘎乱杀

Python 后端 入门 开发语言 零基础

蓝易云 - Linux常用命令cupsdisable命令

百度搜索:蓝易云

云计算 Linux unix 云服务器 cupsdisable

RaftKeeper v2.1.0版本发布,性能大幅提升!

京东零售技术

大数据 企业号2024年7月PK榜 RaftKeeper

Downie 4 发布:全能视频下载神器,一键畅享高清世界!

Rose

Autodesk AutoCAD 2021中文版详细图文安装教程 mac/win

Rose

观《中国数据库前世今生》,体验 TencentDB 技术的崛起。

不惑

tencentdb MySQL 8.0 中国数据库前世今生

Native Instruments Traktor Pro 数字DJ混音器 mac软件

Rose

英特尔56周年:从硅谷走向AI时代

E科讯

解读GaussDB(for MySQL)灵活多维的二级分区表策略

华为云开发者联盟

MySQL 数据库 后端 华为云开发者联盟 企业号2024年7月PK榜

Alarm Clock Pro for mac(闹钟和时间管理工具) v15.5激活版

Rose

革新技术架构,华为云DTSE助力紫藤科技迁移上云经验分享

华为云开发者联盟

数据库 sql 云原生 华为云开发者联盟 企业号2024年7月PK榜

通过 EMR Serverless Spark 提交 PySpark 流任务

阿里云大数据AI技术

大数据 spark 分布式计算 EMR

一个 Golang 项目的测试实践全记录_文化 & 方法_轩脉刃_InfoQ精选文章