测试是避免事故的最后一道屏障。
1 分类
回归测试 -> 集成测试 -> 单元测试
从左到右覆盖率逐渐变大,成本逐渐降低。
1.1 单元测试
1.1.1 单元测试-规则
所有测试文件以_test.go 结尾
func TestXxx(*testing.T)
初始化逻辑放到TestMain
1.1.2 单元测试-例子
1.1.3 单元测试-运行
go test [flags][packages]
1.1.4 单元测试-assert
1.1.5 单元测试-覆盖率
tips:
一般覆盖率:50%~60%,较高覆盖率80%+。
测试分支相互独立,全面覆盖。
测试单元粒度足够小,函数单一职责。
1.2 单元测试-依赖
外部依赖 => 稳定&幂等
1.3 单元测试-文件处理
1.4 单元测试-Mock
monkey: github.com/bouk/monkey
快速Mock函数:
1 为一个函数打桩(用一个函数A去替换函数B,B是原函数,A是打桩函数)
2 为一个方法打桩。
1.5 基准测试
优化代码,需要对当前代码分析。
内置的测试框架提供了基准测试的能力。
1.5.1 基准测试-例子
rand 随机选择函数
fastrand 快速选择随机函数
<>> go test -bench=.
2 项目实践
2.1 需求描述
社区话题页面
展示话题(标题,文字描述)和回帖列表
暂不考虑前端页面实现,仅仅实现一个本地web服务
话题和回帖数据用文件存储
2.2 需求用例
浏览消费用户
2.3 ER图-Entity Relationship Diagram
话题和帖子联系
2.4 分层模型
数据层:数据Model,外部数据得增删查改(service层不需要关心具体的底层的数据的存取,它只需要返回一个Model数据)
逻辑层:业务Entity,处理核心业务逻辑输出(通过接受Repository层得数据,做打包封装,会输出一个实体,Entity[话题页面])
视图层,视图view,处理和外部的交互逻辑(对上游负责,包装一些数据格式,格式化一个结果,做一些封装,然后通过API的形式最终访问)
3 组件工具
Gin高性能go web框架
Go Mod
go mod init
go get gopkg.in/gin-gonic/gin.v1@v1.3.0
安装过程中的问题:出现cannot find module providing package github.com/gin-gonic/gin: working directory is not part of a module错误
解决方案:
go mod init gin
go mod edit -require github.com/gin-gonic/gin@latest
go mod vendor
5 Repository
Topic
QueryTopicByld
Post
QueryPostsByParentld
5.1 Repository-index
var (
topicIndexMap map[int64] *Topic
postIndexMap map[int64][ ] *Post
)
实例:初始化话题数据索引
查询
索引:话题ID
数据:话题
实现查询:
有了索引直接根据id和t直接读取数据。
sync.Once 关键字:适合在高并发场景下,只执行一次的场景(范例模式),可以减少存储的浪费。
索引:话题ID
数据:帖子列表
然后上送给逻辑层,逻辑层进行一个封装。
6 Service
实体:
流程:
参数校对-->准备数据-->组装实体
定义实体:命名为PageInfo(页面信息)
有两个实体一个Topic,另一个PostList数组。
service层主要是为了package PageInfo
代码流程编排
checkParam函数是为了对id进行校验.
prepareInfo实现:获取Responsitory层相应的话题数据和回帖列表数据
并行处理
用wg.Wait()进行阻塞,来等待话题信息和回帖信息,从Responsitory层返回。
7 Controller
构建View对象
业务错误码
包装了一个PageData函数,定义了Code、Msg来表示错误得定义。
8 Router(接口)
初始化数据索引(通过遍历post、ropic文件,生成内存max索引)
初始化引擎配置(Default Inpage)
构建路由(URL路由)
启动服务
9 运行
运行测试
go run server.go
10 高质量编程
目标:
编写更简洁清晰的代码
常用Go语言程序优化手段
熟悉Go程序性能分析工具
了解工程中性能优化的原则和流程
10.1 高质量编程简介
定义:编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码。
各种边界条件是否考虑完备
异常情况处理,稳定性保证
易读易维护
10.1.1 编程原则
简单性 :消除“多余的复杂性”,以简单清晰的逻辑编写代码;
不理解的代码无法修复改进。
可读性:代码是写给人看的,而不是机器;
编写可维护代码的第一步是确保代码可读。
生产力:团队整体工作效率非常重要。
10.2 编码规范
代码格式、注释、命令规范、控制流程、错误和异常处理
10.2.0 代码格式
推荐使用gofmt自动格式化代码:常见IDE都支持方便的配置
goimports 也是,除了格式外,他也能对依赖包进行管理,自动增删依赖的包引用、将依赖包按字母序排列并分类。
10.2.1 注释
作用:
1.解释代码作用;2.解释代码是如何做的;3.解释代码实现的原因;4.解释代码什么情况会出错。
公共符号始终要注释。
例外:不需要注释实现接口的方法,具体不要像下面这样做:
10.2.2 命令规范
variable:简洁胜于冗长
缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写
例如:ServeHTTP而不是ServerHttp
使用XMLHTTPRequest或者xmlHTTPRequest
变量距离其被使用地方越远,则需要携带越多的上下文信息。例如全局变量。
package:
只由小写字母组成。不包含大写字母和下划线等字符
简短并包含一定的上下文信息,例如schema、task等
不要与标准库同名。例如不要使用sync或者strings
10.2.3 控制流程
避免嵌套,保持正常流程清晰
如果两个分支中都包含return语句,则可以去除冗余的else
尽量保持正常代码路径为最小缩进
优先处理错误情况/特殊情况,尽早返回或继续循环来减少嵌套。
故障问题大多数出现在复杂的条件语句和循环语句中。
10.2.4 错误和异常处理
简单错误:表示仅仅出现一次的错误,且在其他地方不需要捕获该错误
优先使用errors.New来创建匿名变量来直接表示简单错误。
如果有格式化的需求,使用fmt.Errorf。
错误的Warp和Unwrap
错误的Wrap实际上是提供了一个error嵌套另一个error的能力,从而生成一个error的跟踪链
在fmt.Errorf中使用:%w关键字来将一个错误关联至错误链中。
errors.Is
可以用来判定是否为特定错误
不同于使用==,使用该方法可以判定错误链上的所有是否含有特定的错误
error.As
在错误链上,获取特定种类的错误,使用errors.As
panic
不建议在业务代码中使用panic
调用函数不包含recover会造成程序崩溃
若问题可以被屏蔽或解决建议使用error代替panic
当程序启动阶段发生不可逆转的错误时,可以在init或main函数中使用panic
recover
recover只能在被defer的函数中使用
嵌套无法生成
只在当前goroutine生效
defer的语句是后进先出
小结
程序会先输出什么?
defer语句会在函数返回前调用
多个defer语句是后进先出
正确答案是31