这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
Go进阶
并发vs并行
Go 可以充分发挥多核优势,高效运行
Goroutine
Goroutine启用简单,使用go func(){}就能启动
CSP(Communicating Squential Processes)
Go协程使用的是通信共享内存,即左图所示,使用的是Channel通道的形式来,保证go协程之间的先入先出。右图使用的是共享内存的形式进行通信,其缺点是需要加锁来保证不出现竞态,效率较低。
channel
make(chan int)无缓冲通道
make(chan int,2 )有缓冲通道
这里dest使用的是有缓冲的通道的原因,消费者的速度较慢,生产者速度快,所以带缓冲就不会影响生产者的执行效率。
并发安全lock

WaitGroup
waitgroup可以保证主协程和其他协程之间能正常退出。
Go依赖管理
Go的依赖管理有三个阶段Gopath Govendor和Gomodule
目的
- 不同环境依赖的版本不同
- 控制依赖库的版本
Gopath
优点是很方便的将所有包都统一管理
缺点:

Go Vendor

Go Module
- 通过
go.mod文件管理依赖包版本 - 通过
go get/go mod指令工具管理依赖包
- 配置文件,依赖描述 go.mod
- 中心仓库管理依赖库 Proxy
- 本地工具 go get/mod
indirect 表示模块的间接依赖
- 主版本2+模块会在模块路径增加/VN 后缀。
- 对于没有 go.mod 文件并且主版本2+的依赖,会+incompatible
Proxy使用的出现
-
无法保证构建稳定性增加/修改/删除软件版本
-
无法保证依赖可用性删除软件
-
增加第三方压力代码托管平台负载问题

go get使用
go mod 使用

GO测试
测试的重要性,保证不发生事故 测试有三种
- 回归测试
- 集成测试
- 单元测试
单元测试

单元测试规则
- 所有测试文件以_test.go 结尾
- 测试函数为
func TestXxx(*testing.T) - 初始化逻辑放到 TestMain 中 Example
#hello.go
package unit_test
func HelloTom() string {
return "Tom"
}
#hellov1_test.go
package unit_test
import "testing"
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
if output != expectOutput {
t.Errorf("Expected %s do not match actual %s", expectOutput, output)
}
}
单元测试覆盖率
-
如何衡量代码是否经过了足够的测试?
-
如何评价项目的测试水准?
-
如何评估项目是否达到了高水准测试等级?
代码覆盖率
在终端中输入
go test hello_test.go hello.go --cover
可以查看测试代码和源代码的覆盖率
-
一般覆盖率:50%~60%,较高覆盖率80%+。
-
测试分支相互独立、全面覆盖。
-
测试单元粒度足够小,函数单一职责。
单元测试-依赖
单元测试需要稳定性和幂等性,即每次测试结果要一样,各函数之间要求隔离。
mock example
func Patch为一个函数打桩
func Unpatch为一个函数卸桩
# deal.go
func ReadFirstLine() string {
open, err := os.Open("log")
if err != nil {
fmt.Printf("err: %v\n", err)
}
defer open.Close()
if err != nil {
return ""
}
scanner := bufio.NewScanner(open)
for scanner.Scan() {
return scanner.Text()
}
return ""
}
func ProcessFirstLine() string {
line := ReadFirstLine()
destLine := strings.ReplaceAll(line, "11", "00")
return destLine
}
#deal_test.go
func TestProcessFirstLineWithMock(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
assert.Equal(t, "line000", line)
}
对ReadFirstLine打桩测试,不再依赖本地文件。我们使用了 monkey.Patch() 方法来替换ReadFirstLine 函数,在测试用例中调用 ReadFirstLine 函数时,实际上调用了我们mocking的函数.在测试结束后使用 monkey.Unpatch() 方法恢复原来的函数。
注意: 使用monkey进行单元测试不是很推荐,因为它会更改了函数的行为,而不是只是测试函数的输入输出,这样的话会导致更多.
总结
在本次学习中学习到了goroutine的使用包括通道、锁和waitgroup等方法来控制资源竞争。 单元测试是生命线,通过单元测试可以防患于未然,有效减少项目上线时出错的概率。 mock包能去除掉测试函数与其他文件资源的耦合。