海量数据高并发场景 构建Go+ES8企业级搜索微服务含资料

#1

download:海量数据高并发场景 构建Go+ES8企业级搜索微服务含资料

Golang-单元测试和模拟框架的介绍和推荐
简介:探索golang的单元测试框架,看看哪个框架与业务经验结合得更好。
我会在标题中标注推荐帧和不推荐帧。如果没有标注,说明体验一般,但是没有特别的缺点。我将拭目以待。
一、单元测试框架介绍
1.本地测试
1.1示例
func TestModifyArr(t *测试。T) {
arr := [3]int{0,1,2}
修改(Arr)
if 112233 == arr[0] {
T.logf ("[testmodifyar] test成功修改了数组元素!")
} else if 0 == arr[0] {
T.errorf ("[testmodifyar] test未能修改数组元素!元素未修改”)
}否则{
T.errorf ("[testmodifyar] test未能修改数组元素!未知元素:%d ",arr[0])
}
}
复制代码
注意:在使用t.Errorf时,单个测试也会被设置为失败(但是测试不会立即停止,只有FailedNow或Fatalf才会停止)
1.2扩展:表格驱动的设计思想
其实就是把多个测试用例封装成一个数组,依次执行同一个测试逻辑。
即使是其他测试框架,这种设计思路也是相当有用的,用例多的时候可以简化代码量。
示例:
风险值(
pow tests =[]结构{
基础浮动64
电源浮动64
预期浮动64
}{
{1, 5, 1},
{2, 4, 16},
{3, 3, 27},
{5, 0, 1},
}
)

//测试一些数学包的计算方法
func testmathppkgmethodbytesting(t * testing。T) {
对于索引,当前测试:=范围功率测试{
if currentTest.expected!=数学。Pow(当前测试基准,当前测试功率){
t.errorf("[TestMathPkgMethod]% d th test:%.2f % . 2f的幂不是预期的:% . 2f “,
index,currentTest.base,currentTest.power,currentTest.expected)
}
}
t.Logf(”[TestMathPkgMethod]所有测试都通过了!")
}
复制代码
1.3平行测试
使用方法:在测试代码中执行:t.Parallel(),测试方法可以和其他测试用例并行执行。
场景:一般需要同时执行多个用例,比如测试生产和消费。
但个人不建议这么做,因为这有点违背“单一测试”的概念:单一测试测试一个功能。类似的场景也可以通过在单次测量中设置通道多流程来实现。
2、goconvey
2.1示例
介绍方法:
去找github.com/smartystreets/goconvey/convey

导入模式:
导入(
。" github . com/smarty streets/go convey/convey "
)

//提醒:goconvey、gomonkey等工具类最好使用这种导入方法,减少使用其内部方法的代码长度,使代码更加简洁。
复制代码
func TestMathPkgMethodByConvey(t *测试。T) {
Convey(" Convey test power ",t,func() {
对于_,当前测试:=范围功率测试{
所以(数学。Pow(currentTest.base,currentTest.power),ShouldEqual,currentTest.expected)
}
})
}
复制代码
So的这种方法结构对于刚接触GoConvey的同学来说可能有点难以理解。下面是源代码的简要说明:
//源代码:github . com \ smarty streets \ go convey @ v 1 . 6 . 4 \ convey \ context . go
类型断言函数(实际接口{},应为…接口{})字符串

func (ctx *context) So(实际接口{},assert断言,应为…接口{}) {
if result := assert(实际,预期…);result == assertionSuccess {
ctx.assertionReport(报告。NewSuccessReport())
}否则{
ctx.assertionReport(报告。NewFailureReport(结果))
}
}
复制代码
关键是对So参数的理解。总共有三个参数:
实际:输入
断言:断言
预期:预期值
通过查看定义,Assert assertion实际上是一种方法,但事实上,Convey包已经帮助我们定义了大多数基本断言:
//源代码:github . com \ smarty streets \ go convey @ v 1 . 6 . 4 \ convey \ assertions . go
风险值(
ShouldEqual =断言。肩平等
ShouldNotEqual =断言。不应相等
ShouldAlmostEqual =断言。应该差不多相等
ShouldNotAlmostEqual =断言。ShouldNotAlmostEqual
should like =断言。应该像
ShouldNotResemble =断言。不应该相似

复制代码
可以直接使用等于、大于或小于等判断方法。
2.2双重嵌套
func TestMathPkgMethodByConvey(t *测试。T) {
//双重嵌套
Convey("Convey test multiple test “,t,FailureHalts,func() {
Convey(“测试失败”,func() {
所以(数学。Pow(5,2),ShouldEqual,26)
日志。printf(”[测试] 5^3 = 125?要执行!”)
所以(数学。Pow(5,3),ShouldEqual,125)
})

Convey(“成功测试”,func() {
日志。printf("[测试] 5^2 = 25?要执行!”)
所以(数学。Pow(5,2),ShouldEqual,25)
})
})
}
复制代码
注意:不再需要将测试对象添加到传送带的内层。
注意:子传送的执行策略是并行的,因此前一个子传送的失败不会影响后续的传送。但是一个孩子被连续处决。
2.3跳过测试
如果在这次提交中有些测试没有被完全测试,您可以使用TODO+首先跳过这些测试,首先进行注释,然后在下一次提交时改进它们。
SkipConvey:跳过当前调查下的所有测试。
SkipSo:跳过当前断言
2.4设置失败后的执行策略
默认情况下,一个Convey下的多个So断言是失败后终止的策略。如果要调整,只需将失败策略添加到Convey参数中即可。例如,如果设置失败,请继续,然后使用失败继续。
//源代码:github . com \ smarty streets \ go convey @ v 1 . 6 . 4 \ convey \ doc . go
常量(

失败继续失败模式= “继续”


故障暂停故障模式= “暂停”


失败继承失败模式= “继承”
)
复制代码
但是,应该注意,这里的故障后策略是针对一个传送下的多个So断言,而不是一个传送下的多个子传送。那么接下来我们就来说说Convey的实现机制:它是并行的。
2.5子传送并发执行原理简介
在go transportation的底层,使用jtolds/gls库实现对goroutine的管理和多个子传输的并发执行。
//源代码:github . com \ smarty streets \ go convey @ v 1 . 6 . 4 \ convey \ context . go
func (ctx *context)传送(项目…接口{}) {

if inner_ctx.shouldVisit() {
ctxMgr。设置值(gls。值{nodeKey: inner_ctx},func() {
//条目。Func是实际的测试方法。
inner _ ctx.conveyInner(条目。情况,进入。Func)
})
}
}

//源代码:github . com \ jtolds \ gls @ v 4 . 20 . 0+不兼容\context.go
func(m * context manager)set Values(new _ Values Values,context_call func()) {

//此方法将确定是否满足并发执行的条件。
EnsureGoroutineId(func(GID uint){
…//解析传入的上下文参数。

context_call()
})
}
复制代码
知识有限,这里就不说gls库的原理了。借助一些文档,我知道gls其实是通过go底层的api来管理GPM模型的,在满足一定条件的情况下,会将子调查提交给子协同流程执行(默认)。
如果你对gls库感兴趣,想知道它的底层是如何管理协作流程的,可以参考:
Gl官方github地址
gls godoc
3.作证(推荐)
其实evidence的用法和native testing差不多,都是对断言的明确定义。
它提供了assert和require两种用法,分别对应失败后的执行策略。前者失败后继续执行,后者失败后立即停止。但都是单断言失败,当前测试失败。
func TestGetStudentById(t * testing。T) {
currentMock := gomonkey。ApplyFunc(dbresource。NewDBController,dbresource。NewDBMockController)
推迟当前时钟。重置()
学校服务。新学校服务()
学生:=学校服务。GetStudentById("1 ")

断言。NotEqual(t,“”,学生。姓名)
要求。Equal(t,studentsql。考试_学生_姓名,学生。姓名)
}
复制代码
4.测试框架概述
下面简单总结一下几个测试框架:个人觉得GoConvey的语法对于业务代码入侵来说有点严重,本身需要一些时间去理解,比如testify的清晰逻辑。单元测试逻辑本身相对简单。综上,更推荐使用evident。
二。模拟框架介绍
1.gostub(不推荐)
1.1基本用途
去找github.com/prashantv/gostub
复制代码
func TestGetLocalIp(t *测试。T) {
//堆积变量
varStub := Stub(&testGlobalInt,100)
延迟变量存根。重置()
日志。printf("[测试模拟]模拟变量:%d ",testGlobalInt)

//堆积方法
var getIpFunc = system。GetOutboundIP
funcStub := StubFunc(&getIpFunc," 1.2.3.4 ")
延迟funcStub。重置()
}
复制代码
1.2与GoConvey组合的示例

1.3不推荐使用的原因
主要的限制太多了:
Gostub:因为方法的mock要先声明变量才能被mock,所以连接口方法都需要这样定义,不是很方便。
此外,如果您需要一个mock方法,并且参数和返回的数量是可变长度的数组类型,您可能无法定义mock。
最后,同样的方法,如果需要模拟多个场景,gostub无法实现。这个很麻烦,mock的不同参数场景应该算是mock的基本功能。
2、gomock
官方维护的mock框架,只要是对象+接口的数据结构,基本都可以通过gomock直接写不同场景的mock。
之前写过一篇关于如何使用gomock的基础介绍。一般来说更适合框架场景,比如protobuf定义生成的外部对象和接口。如果能自动生成gomock代码,开发起来会更方便。但不是特别适合业务代码,因为业务内部往往定义了很多对象,为每个对象生成mock有点麻烦。

3.1打桩方法
func TestGetAbsolutePath(t *测试。T) {
//打桩方法
funcStub := ApplyFunc(config。GetAbsolutePath,testGetAbsolutePath)
延迟funcStub。重置()
日志。Printf(“配置路径:%s “,config。GetAbsolutePath())
}
复制代码
总的来说和gostub的使用很像,就是方法要通过变量单独指定,mock要设置。执行ApplyFunc方法
区别在于StubFunc直接定义方法的参数(行为结果),而ApplyFunc还需要定义方法的具体动作(行为本身)。
3.2用顺序堆法。
func TestGetAbsolutePath(t *测试。T) {
//方法序列堆叠
retArr:=[]输出单元格{
{Values: Params{”。/testpath1”}},
{Values: Params{"。/testpath2"}},
{Values: Params{"。/testpath3"},次数:2,
}
ApplyFuncSeq(配置。GetAbsolutePath,retArr)

日志。Printf("配置路径:%s ",config。GetAbsolutePath())
日志。Printf("配置路径:%s ",config。GetAbsolutePath())
日志。Printf("配置路径:%s ",config。GetAbsolutePath())
日志。Printf("配置路径:%s ",config。GetAbsolutePath())
}
复制代码
3.3全局变量的打桩

用法类似于gostub的Stub方法,不赘述。
此外,还有ApplyMethod(为对象的指定方法打桩)、ApplyMethodSeq等。,而且用法还是很像ApplyFunc的。您可以详细阅读参考博客,或者只查看源代码中的测试示例。
四。总结与展望
介绍了几种常见的单测和mock框架的使用方法,得出了evidence+gomonkey是一种比较直观易用的框架的结论。