当前位置: 首页 > news >正文

苏州微网站建设小程序sdk开发

苏州微网站建设,小程序sdk开发,东莞企业网站费用,公司网站建设意见和建议文章目录 一、前言1、单测的定位2、vscode中生成单测 二、构造测试case的注意事项1、项目初始化2、构造空interface{}3、构造结构体的time.Time类型4、构造json格式的test case 三、运行单测文件1、整体运行单测文件2、运行单个单测文件报错#xff08;1#xff09;command-l… 文章目录 一、前言1、单测的定位2、vscode中生成单测 二、构造测试case的注意事项1、项目初始化2、构造空interface{}3、构造结构体的time.Time类型4、构造json格式的test case 三、运行单测文件1、整体运行单测文件2、运行单个单测文件报错1command-line-arguments是什么2undefined发生原因3缺少初始化导致的发生panic 3、查看单测覆盖率4、单测覆盖文件解读5、生成可被浏览器打开的单测文件6、单测覆盖率的问题 四、关于单测粒度的问题1、chatgpt的回答2、个人理解 五、mock数据1、mock组件选择2、mock实操1mock函数调用2mock方法调用3mock其他包的函数4mock循环中的函数5mock http调用 3、对于mock的看法 一、前言 1、单测的定位 单测在软件工程中的地位毋庸置疑它要求工程师必须去主动思考代码的边界异常处理等等。另一方面它又是代码最好的说明书你的函数具体做了什么输入和输出一目了然。 计算机科学家Edsger Dijkstra曾说过“测试能证明缺陷存在而无法证明没有缺陷。”再多的测试也不能证明一个程序没有BUG。在最好的情况下测试可以增强我们的信心代码在很多重要场景下是可以正常工作的。 参考go语言圣经之测试函数 2、vscode中生成单测 参考在 VS Code 快速生成单元测试 vscode生成单元测试如下我们需要编写测试用例数组明确指出来want结果以及wantErr,通过遍历的方式去执行测试用例数组。 func TestGenerateStsTokenService(t *testing.T) {type args struct {ctx context.ContextgenerateStsData *dto.GenerateStsReqParams}tests : []struct {name stringargs argswantResp *common.RESTRespwantErr bool}{{name: 测试正常生成sts,args: args{ctx: context.TODO(),generateStsData: dto.GenerateStsReqParams{SessionName: webApp,AuthParams: dto.AuthParamsData{},},},wantResp: common.RESTResp{Code: 0,Data: dto.OssStsRespData{},},wantErr: false,},{name: 测试异常生成sts,args: args{ctx: context.TODO(),generateStsData: dto.GenerateStsReqParams{SessionName: liteApp,AuthParams: dto.AuthParamsData{},},},wantResp: common.RESTResp{Code: 20003,Data: interface{}(nil),},wantErr: true,},}for _, tt : range tests {t.Run(tt.name, func(t *testing.T) {gotResp, err : GenerateStsTokenService(tt.args.ctx, tt.args.generateStsData)if (err ! nil) ! tt.wantErr {t.Errorf(GenerateStsTokenService() error %v, wantErr %v, err, tt.wantErr)return}if !reflect.DeepEqual(gotResp, tt.wantResp) {t.Errorf(GenerateStsTokenService() %v, want %v, gotResp, tt.wantResp)}})} }二、构造测试case的注意事项 1、项目初始化 // TestMain会在执行其他测试用例的时候自动执行 func TestMain(m *testing.M) {setup() //初始化函数retCode : m.Run() // 运行单元测试teardown() //后置校验钩子函数可不实现os.Exit(retCode) //清理结果 }2、构造空interface{} // 直接给Data赋值为nil的话验证会失败 // 单纯的nil和(*infra.QueryOneMappingCode)(nil)是不一样的 wantResp: common.RESTResp{Code: 0,Message: ,Data: (*infra.QueryOneMappingCode)(nil),},// 数组类型的空 // []dto.OneMappingCode{}也会验证失败 wantRes: []dto.OneMappingCode(nil),3、构造结构体的time.Time类型 Data: infra.xxx{ID: 54,Code: 338798,TakerUid: ,State: 1,Type: 1,CreatedAt: time.Date(2023, time.June, 9, 16, 32, 59, 0, time.Local),},也可以直接打印接口的返回看看CreatedAt返回的是什么然后构造一下就可以。 t.Logf(gotResp:(%#v), gotResp.Data)4、构造json格式的test case wantResp: common.RESTResp{Code: 0,Message: success,Data: {id: 54,code: 338798,creator_uid: 12345,client_appId: 1234,taker_uid: ,state: 1,type: 1,created_at: 2023-06-09T16:32:5908:00},}, 三、运行单测文件 1、整体运行单测文件 cd /xxx 单测目录go test成功输出PASSok2、运行单个单测文件报错 错误提示如下 # command-line-arguments [command-line-arguments.test] ./base_test.go:26:18: undefined: Ping明明Ping函数和单测文件都在同一个包下面为什么会出现undefined呢command-line-arguments是什么 答 1command-line-arguments是什么 go test [flags] [packages] [build flags] [packages] 命令行参数中指定的每个包或文件都将被视为一个要进行测试的包。而 command-line-arguments 这个标识符就是用来表示上述情况中命令行参数中指定的文件。这样可以使 go test 命令将指定的文件作为单独的包进行处理并执行其中的测试函数。2undefined发生原因 错误提示build失败也就是说我们需要把单测文件依赖的文件也传入进去。比如我这里单测base_test.go文件则需要把base.go也写到命令行参数中。 具体参考【Golang】解决Go test执行单个测试文件提示未定义问题 go test ./base.go ./base_test.go3缺少初始化导致的发生panic 一般来说我们在一个package下定义一个TestMain()函数就可以了进行代码的初始化。但是当我们需要运行单个测试文件的时候有可能这个测试文件里面恰好没有TestMain()了咋整。 api_test.goTestMain() base_test.go // 没有TestMain()函数// 解决方案 1、初始化代码放到setup()函数中 2、go命令行 go test ./base.go ./base_test.go ./api_test.go ./api.go 3、只想运行base_test.go怎么办base_test.go中加上自己的setuoBase()3、查看单测覆盖率 go test -covercoverage: 80.4% of statements4、单测覆盖文件解读 go test -coverprofilecoverage.out// 打开单测覆盖率文件 mode: set base.go:10.118,14.23 3 1 base.go:14.23,17.3 2 1解释如下10.118,14.23 3 1 表示第 10 行到第 14 行代码被测试覆盖到了且覆盖率为 3/1 (即 300%)。这是因为第 10 行至少执行了一次如果执行了三次则覆盖率为 300%。14.23,17.3 2 1 表示第 14 行到第 17 行代码被测试覆盖到了且覆盖率为 2/1 (即 200%)。5、生成可被浏览器打开的单测文件 go test -coverprofilecoverage.out go tool cover -htmlcoverage.out -o coverage.html绿色代表被覆盖到的代码红色代表没有被覆盖到的代码。 左上角是运行单测命令目录下所有go文件的覆盖率。 可以考虑新增单测case来覆盖到这部分红色。 6、单测覆盖率的问题 覆盖率为 100% 表示测试用例覆盖了所有的可能执行路径即程序的所有功能都被覆盖到了。而覆盖率高于 100% 则表示相同的代码路径被多次测试或某些代码行在被测试期间被执行了多次。 但是单测100%并不能保证没有bug只能保证写出来的代码没问题但逻辑或者业务上的漏洞是检测不到的。 博主在滴滴的组是建议单测覆盖率50%以上其他朋友的公司要求核心接口必须有单测整体单测覆盖率30%以上。有需要的可以参考下。 四、关于单测粒度的问题 写单测的时候总会疑问到底要写的多细呢特别是原来项目没有单测的时候补单测的代码比业务逻辑代码还多。。。 本例中目录结构如下 domain:base.gocode.gocode_test.goutil.gocode.go会调用base.go和util.go的函数运行code_test.go发现单测覆盖率 已经80%了是不是意味着只需要写个code_test.go就可以了呢 1、chatgpt的回答 实际上不是的base.go和util.go后续还可能被其他的文件使用我们写单测的时候应该尽量覆盖所有的异常情况也就是程序的边界问题。因此base.go和util.go也需要做对应的单测这样才能得到高质量的代码。 2、个人理解 单个code_test.go文件导致的问题是下层函数不mock可能会影响到实际的数据导致单测只能运行一次而不能一直PASS。其次是代码流程变长导致单测case越写越多接近集成测试了这不是我们单测的目标。 把code_test.go中关于base.go和util.go的函数都给mock掉发现单测覆盖率只有37%且测试路径比较短。还需要分别写base_test.go和util_test.go写完util_test.go单测覆盖率立马82%。 拆分的粒度变细更加关注每个函数的输入和输出。特别是当修改某个函数的时候只需要使用对应的单测来进行验证而不需要从入口处进行测试。毕竟单元测试不是集成测试。 参考 Golang 单元测试有哪些误区和实践 Go的单元测试技巧 五、mock数据 在写单测的时候程序难免会出现各种跨文件的函数调用以及操作第三方中间件或者上下游交互的情况这个时候mock就显得尤为重要。 想象下没有mock的时候我们运行单测可能就会写入一次数据库或者对下游发起一次请求这样的单测怕是只能运行一次哟。mock的出现让我们关注代码的实现细节不会担心会造成数据污染或者单测只能运行一遍就GG的情况。 1、mock组件选择 参考如何做好单元测试Golang Mock”三剑客“ gomock、monkey、sqlmock GO进阶单元测试 博主这里更喜欢无侵入的mock直接一把梭。可惜monkey已经不更新了现在都是用gomonkey,国人大佬开发的 gomonkey 项目库 解析 Golang 测试8- gomonkey 实战 2、mock实操 1mock函数调用 函数中存在大量的封装调用比如A-BA-C这种因此自由mock B和C函数对我们的单元测试来说还是很重要的。 patches : gomonkey.ApplyFunc(queryOneMappCode, func(ctx context.Context, code string) (*infra.QueryOneMappingCode, error) {// 参数大于6则返回空if len(code) 6 {return nil, nil}return infra.QueryOneMappingCode{ID: 54,Code: 338798,CreatedAt: time.Date(2023, time.June, 9, 16, 32, 59, 0, time.Local),}, nil})defer patches.Reset()2mock方法调用 1、实例化接口 var mockProvider provider.Test // 接口如下 type TestDbProvider interface {SetDb(db *sqlx.DB)GetOne(dest interface{}, sql string, args interface{}) (resp *infra.QueryOneMappingCode, err error) }2、mock对应的查询方法 // 注意第一个参数不能是指针不然mock会失效 // 例如 var oss_bucket_obj *oss.Bucket 传入target为: *oss_bucket_obj // 传地址会报错 patches : gomonkey.ApplyMethodFunc(mockProvider, GetOne, func(dest interface{}, sql string, args interface{}) (resp *infra.QueryOneMappingCode, err error) {code : args.(string)if code 123456 {return infra.QueryOneMappingCode{ID: 1,Code: 123456,CreatedAt: time.Date(2023, time.June, 9, 16, 32, 59, 0, time.Local),}, nil} else if code 456789 {return infra.QueryOneMappingCode{ID: 1,Code: 456789,CreatedAt: time.Date(2023, time.June, 9, 16, 32, 59, 0, time.Local),}, nil} else {return nil, nil}})defer patches.Reset()3mock其他包的函数 在xx_test文件中直接引用其他包即可。一般xx_test.go和xx.go在同一个包下所以也不用担心出现循环引用的问题。 patches : gomonkey.ApplyFunc(util.GenerateRandomCode, func(numDigits int) string {return 123456})defer patches.Reset()4mock循环中的函数 比如在A函数中循环3次调用了B函数那么mock如下 createA : infra.CreateMappingCode{Code: 933903}createB : infra.CreateMappingCode{Code: 601690}createC : infra.CreateMappingCode{Code: 798493}p : gomonkey.ApplyFuncSeq(structureMappingCodeRecord, []gomonkey.OutputCell{{Values: gomonkey.Params{createA}},{Values: gomonkey.Params{createB}},{Values: gomonkey.Params{createC}},})defer p.Reset() // 恢复原始函数5mock http调用 // vscode自动生成的test代码 for _, tt : range tests {t.Run(tt.name, func(t *testing.T) {// mock httptestts : httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {if r.Method ! http.MethodGet {w.WriteHeader(http.StatusNotFound)}// 构造返回参数w.WriteHeader(http.StatusOK)// 获取POST请求的参数根据参数返回不同的响应bodyBytes, err : io.ReadAll(r.Body)if err ! nil {// 处理错误w.WriteHeader(http.StatusBadRequest)}// 获取post参数params : new(dto.GenerateStsReqParams)json.Unmarshal(bodyBytes, params)// 根据传递的参数返回不同的响应res : new(common.RESTResp)if params.SessionName webApp {res common.RESTResp{Code: 0,Message: success,Data: dto.OssStsRespData{Region: hangzhou,Bucket: test,},}} else {res common.RESTResp{Code: 1,Message: failed,Data: dto.OssStsRespData{},}}// 模拟接口的返回http接口返回是字节数据因此需要json.MarshaljsonStr, _ : json.Marshal(res)w.Write(jsonStr)}))defer ts.Close()// 替换原来的url为mock的urlGenerateOssStsUrl ts.URL// 发起请求请求中的http会被mock掉gotResp, err : GenerateStsTokenService(tt.args.ctx, tt.args.generateStsData)if (err ! nil) ! tt.wantErr {t.Errorf(GenerateStsTokenService() error %v, wantErr %v, err, tt.wantErr)return}t.Logf(gotResp:(%#v) ,wantResp:(%#v), gotResp, tt.wantResp)if !reflect.DeepEqual(gotResp, tt.wantResp) {t.Errorf(GenerateStsTokenService() %v, want %v, gotResp, tt.wantResp)}})}3、对于mock的看法 对于mock有以下两种态度 一方的人主张不要滥用mock能不mock就不mock。被测单元也不一定是具体的一个 函数可能是多个函数本来就应该串起来必要的时候再mock。一方则主张将被测函数所有调用的外面函数全部mock掉只关注被测函数自己的 一行行代码只要调用其他函数全都mock掉用假数据来测试。本来处于懒惰和少写单测的角度我是支持第一种方式的。 例如 单测函数A函数 内部逻辑A-B : B函数全是业务逻辑A-C : C函数包括mysql或者redis操作A-D-E: D函数纯业务逻辑构造请求参数。E函数对外发起http请求第一种方式是只mock C和E函数测试A函数的时候会把B和D也测试到。主打一个省事快捷。 直到我遇到了更复杂的场景B里面还有B1和B2函数D里面有D1和D2函数逻辑非常复杂的情况下第一种方式就变成了集成测试。单测用例慢慢变成了测试用例。 比如只修改D2函数的情况下要修改和通过单测A进行测试。。。。 第二种方式就是在每一层都mock掉外部调用。单测A就只关注A的逻辑mock掉B,C,D,E只关注B,C,D,E输出是正确或者错误的情况。 针对B,C,D,E函数又有自己的单测函数充分覆盖掉。这样当修改D2函数的时候只需要修改和通过D2的单测即可。 对于外部依赖比如第三方库mysql,redis,mq这种统一进行mock。 对于内部的函数调用建议是粒度细一些A_test.go就只对A.go里面的逻辑负责。至于调用B.go的部分就交给B_test.go吧。 end
http://www.dnsts.com.cn/news/32809.html

相关文章:

  • 创意工作室网站阿里云网站域名查询
  • 上海专业网站建设网站餐饮业网站建设招标书
  • 网站开发有什么好的命题弹幕视频网站开发
  • 门户网站自查报告建筑网校排名前十的品牌
  • 保定企业免费建站手机网站打开速度
  • 可信网站认证有用吗一个网页的制作流程
  • 网站更换目录名如何做301跳转网站建设 制作公司
  • 绿色网站建设背景的原因wordpress反向代理
  • 怎么用阿里云服务器做淘客网站网站tag页面如何做
  • 北京房产网站大全怎么创建自己的官网
  • 网站域名代理备案怎样学好动漫制作专业
  • 如何建设 营销型 网站织梦 营销型网站
  • 青岛网站制作方法买权重网站
  • 计算机网络设计实验报告北京seo优化wyhseo
  • 企业快速建站免费模板wordpress需要登录密码
  • net网站开发做手工简笔yellow最新免费观看
  • 什么公司做企业网站网络营销的定义和特点
  • 河南今天发生的重大新闻seo咨询
  • 营销型网站建设宣传语如何优化一个网站
  • 巴中微小网站建设案例招标代理公司注册
  • 电商网站的分辨率北京网页制作公司
  • 广州建站公司有哪些天津业之峰装饰公司官网
  • 网站制作设计多少钱交通局网站建设方案
  • 网站开发模块就业前景p2p做网站
  • 网站开发的完整流程图抽奖网站怎么制作
  • 海口网站建设方案中国最顶尖的服装设计公司
  • 谁可以做网站沈阳市网站制作公司
  • php怎样做网站制作网站免费建站
  • ip做网站地址中国十大外贸平台
  • 怎么在vmware上做网站南阳旅游网站建设现状