Golang依赖管理、测试与高质量编程

urlyy

依赖管理

Golang的依赖管理经历了path -> vendor -> mod三个阶段。很幸运在我开始学go的时候,已经有go mod了(虽然还是要手动设置module)

为什么要演变呢?其实就是依赖的版本问题。

  • path。早期的golang要求所有的源码必须放在$GOPATH/src下,拉取的第三方代码也要放在这下面。且当时没有版本的概念,很容易出现名称冲突、版本冲突的情况,非常混乱。
  • vendor。vendor直接将第三方库拷贝了一份放在当前项目下,这就不需要将依赖来源设为本地所有项目的第三方库了,只需要查看当前项目的vendor文件夹内。但是这只是单纯的拷贝,没有复用一些公共库,对于外部依赖的变更与版本信息也不能很好的感知到,包升级存在无法评估的风险。
  • mod。module模式允许项目放在任意位置了,同时给出了go.mod这种类似pom.xmlpackage.json的记录依赖和版本信息的文件。在文件中除了依赖名和版本号,还有如incompatiblereplaceexclude等关键词来起到辅助管理的作用。使用起来也很简单。go mod initgo mod installgo mod tidy,轻松进行依赖管理和下载!

除此之外,go mod也允许做项目内同依赖不同版本的兼容,在这种情况下会选取一个最低的兼容版本作为各项目共用的依赖。

除了工具之外,go允许将依赖路径写为一个url,比如下面这样。使用go mod tidy也会拿到这个依赖的”链接”并下载。这里打引号是因为go没有真的这么做。

1
2
3
4
import (
"github.com/Moonlight-Zhao/go-project-example/util"
...
)

如果直接通过url下载,第一是会增加各大代码托管平台的负载,往大了说已经有点在侵犯人家公司的利益了;第二是如果项目作者突然删除仓库,会导致这个依赖不能被正确访问到。go弄了一个proxy,其实就是对这些网站的代码做了不变的备份。开发者只会访问这个镜像网站,而且第三方依赖不会因为作者的行为而产生较大波动。

测试

单元测试

作为一个后端,也要对自己的模块写单元测试。go提供了良好的工具go test,并且定义了规范,即文件的命名以及测试函数的命名。


他也做了一个美化的输出,便于查看测试结果


除此之外也可以加入参数,比如–cover可以查看覆盖率,众所周知覆盖率是衡量测试好坏的一个重要标准,所以这个也是很好用的一个东西。

Mock

测试的一个基本要求就是满足幂等,即多次进行同一个测试,测试结果必须相同。这应该不难理解。

但是有些被测试模块会依赖外部环境,比如用到了数据库、当前时间、随机数种子等。这就需要我们做一个虚拟环境,测试只会依赖这些我们手动搭建的不变的环境,而不会依赖那些在业务中不断变化的外部环境。

视频中介绍了monkey这个库的用法,原理就是重写了函数定义,虽然单元测试中调用的是业务中涉及的函数,但这个函数已经被monkey替换内容了。

之前有用过go-sqlmock进行数据库的mock,当时还没想过mock是为了幂等,只是以为要让测试不影响上线项目的数据源。看完视频豁然开朗。

基准测试

666,go还为benchmark提供了工具。一般来说,如果要进行简单的性能测试,直接记录开始时间和结束时间,让待测函数跑个几万次,然后拿duration做比较就行。但是专门的benchmark工具显然更加专业,

如下图,直接在go test下再加一个参数就可以进行,会打印当前的环境信息、单次运行的cpu耗时。

高质量编程

这个视频真的干货满满。我想把每页PPT都贴在这,不过还是算了。这里做些总结

  • 保持简单和高可读。这里深有感悟,诸如大一时期学的c语言的骚操作,又或者花里胡哨的lambda、新语法等,表面上在炫技,实际上在团队项目中是纯纯的毒瘤,只能自我感动,别人看不懂搜API的时候直呼MMP。

  • 函数加注释。这个是众所周知的了,一堆程序语法哪比得上贴心的自然语言说明书呢?但是也有个要求。

    • 公共方法必须注释。这个是必然的,公共方法要让其他模块调的,不管是跳过来阅读还是让IDE直接显示在函数定义的弹窗上,都能让开发者快速了解函数内容。
    • 有时候注释不用写。注释虽好,写废话也不行。如果一个函数名称已经足够表明内容,写注释和读注释只会waste time,搞了个寂寞。
    • 注释要解释代码做了什么、怎么做的、为什么要这么做以及在哪种情况下会产生哪种特定输出或者异常。
  • 记得将代码格式化。虽然golang对一些格式有限制,但是在空格、缩进这些还是不会太多干涉。对于一个团队项目,一个人的特立独行的格式可能会脏了大家的眼睛。所以使用go-fmt工具格式化代码再上传,是个好习惯。

  • 命名风格要随机应变。下面是一些例子:

    • 对于会到处使用的一个变量,名称就要尽量具体
    • 如果是一个for循环的for i,这个i就没必要写成index
    • 比如一个记录结束时间的变量,我们应该取为end_time而不是t
    • 如果一个包名为time,那么函数应该用time.Now()而不是time.NowTime()
      包名有统一的标准,即使用单数、不要与std重名等。
  • 流程控制。这里其实我有点异议,对于这块其实业界也是有争议的,一边说的是只留一个统一的返回位置,另一边说的是特殊情况尽早return。只能说仁者见仁的,这两种我比较倾向于第二种,可以少一些考虑if…else的心智负担,分支嵌套也会少一点,代码看起来会舒服一些,虽然找return确实需要眼睛擦亮一点:)

总结就是注释应该提供代码未标注的上下文信息

最后,很喜欢视频里的两句话:代码是最好的注释以及好的代码有很多注释,不好的代码需要很多的注释

  • 标题: Golang依赖管理、测试与高质量编程
  • 作者: urlyy
  • 创建于 : 2024-11-12 22:46:41
  • 更新于 : 2024-12-06 01:38:45
  • 链接: https://urlyy.github.io/2024/11/12/2024字节青训营笔记-Golang依赖管理、测试与高质量编程/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
Golang依赖管理、测试与高质量编程