Golang教程六(单元测试,反射,网络编程,部署)
目录
一、单元测试
单元测试
子测试
TestMain
二、反射
类型判断
通过反射获取值
通过反射修改值
结构体反射
利用tag修改结构体的某些值
调用结构体方法
orm的一个小案例
对反射的一些建议
三、网络编程
socket编程
websocket编程
四、部署
打包命令
交叉编译
一、单元测试
Go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言的测试框架相似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。通过单元测试,可以解决:
- 确保每个函数是可运行,并且运行结果是正确的
- 确保写出来的代码性能是好的
- 单元测试能及时的发现程序设计或实现的逻辑错误,使问题暴露,便于问题的定位解决,而性能测试的重点在于发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定
Go 语言推荐测试文件和源代码文件放在一块,测试文件以
_test.go
结尾
注意点:
- 测试用例文件名必须以_test.go结尾
- 测试用例函数必须以Test开头,一般来说就是Test+被测试的函数名
单元测试
例如我现在有两个用于计算的文件,叫calc.go
package mainfunc Add(a int, b int) int {return a + b
}func Mul(a int, b int) int {return a * b
}
那么我的测试文件就是calc_test.go
package mainimport "testing"func TestAdd(t *testing.T) {if ans := Add(1, 2); ans != 3 {// 如果不符合预期,那就是测试不通过t.Errorf("1 + 2 expected be 3, but %d got", ans)}if ans := Add(-10, -20); ans != -30 {t.Errorf("-10 + -20 expected be -30, but %d got", ans)}
}
go test // 可以运行某个包下的所有测试用例
-v
参数会显示每个用例的测试结果
-run
参数可以指定测试某个函数
单元测试框架提供的日志方法
方 法 | 备 注 | 测试结果 |
---|---|---|
Log | 打印日志,同时结束测试 | PASS |
Logf | 格式化打印日志,同时结束测试 | PASS |
Error | 打印错误日志,同时结束测试 | FAIL |
Errorf | 格式化打印错误日志,同时结束测试 | FAIL |
Fatal | 打印致命日志,同时结束测试 | FAIL |
Fatalf | 格式化打印致命日志,同时结束测试 | FAIL |
子测试
如果需要给一个函数,调用不同的测试用例,可以使用子测试
子测试里面的Fatal,是不会终止程序的
package mainimport "testing"func TestAdd(t1 *testing.T) {t1.Run("add1", func(t *testing.T) {if ans := Add(1, 2); ans != 3 {// 如果不符合预期,那就是测试不通过t.Fatalf("1 + 2 expected be 3, but %d got", ans)}})t1.Run("add2", func(t *testing.T) {if ans := Add(-10, -20); ans != -30 {t.Fatalf("-10 + -20 expected be -30, but %d got", ans)}})}
如果测试用例很多,还可以用一个类似表格去表示
package mainimport ("testing"
)func TestAdd(t *testing.T) {cases := []struct {Name stringA, B, Expected int}{{"a1", 2, 3, 5},{"a2", 2, -3, -1},{"a3", 2, 0, 2},}for _, c := range cases {t.Run(c.Name, func(t *testing.T) {if ans := Add(c.A, c.B); ans != c.Expected {t.Fatalf("%d * %d expected %d, but %d got",c.A, c.B, c.Expected, ans)}})}
}
TestMain
它是测试的入口
我们可以在TestMain里面实现测试流程的生命周期
package mainimport ("fmt""os""testing"
)// 测试前执行
func setup() {fmt.Println("Before all tests")
}// 测试后执行
func teardown() {fmt.Println("After all tests")
}func Test1(t *testing.T) {fmt.Println("I'm test1")
}func Test2(t *testing.T) {fmt.Println("I'm test2")
}// 必须叫这个名字 测试主入口
func TestMain(m *testing.M) {// 测试前执行setup()code := m.Run()// 测试后执行teardown()os.Exit(code)
}
二、反射
类型判断
判断一个变量是否是结构体,切片,map
package mainimport ("fmt""reflect"
)func refType(obj any) {typeObj := reflect.TypeOf(obj)fmt.Println(typeObj, "+", typeObj.Kind())// 去判断具体的类型switch typeObj.Kind() {case reflect.Slice:fmt.Println("切片")case reflect.Map:fmt.Println("map")case reflect.Struct:fmt.Println("结构体")case reflect.String:fmt.Println("字符串")}
}func main() {refType(struct{ Name string }{Name: "os_lee"})name := "os_lee"refType(name)refType([]string{"os_lee"})
}
通过反射获取值
package mainimport ("fmt""reflect"
)func refValue(obj any) {value := reflect.ValueOf(obj)fmt.Println(value, "+", value.Type())switch value.Kind() {case reflect.Int:fmt.Println("Int=", value.Int())case reflect.Struct:fmt.Println("Interface=", value.Interface())case reflect.String:fmt.Println("String=", value.String())}
}func main() {refValue(struct{ Name string }{Name: "os_lee"})name := "os_lee"refValue(name)refValue([]string{"os_lee"})
}
通过反射修改值
注意,如果需要通过反射修改值,必须要传指针,在反射中使用Elem取指针对应的值
结构体反射
读取json标签对应的值,如果没有就用属性的名称
这个示例很简单,没有处理-和omitempty的情况
package mainimport ("fmt""reflect"
)type Student struct {Name stringAge int `json:"age"`
}func main() {s := Student{Name: "os_lee",Age: 24,}t := reflect.TypeOf(s)v := reflect.ValueOf(s)for i := 0; i < t.NumField(); i++ {field := t.Field(i)jsonField := field.Tag.Get("json")if jsonField == "" {// 说明json的tag是空的jsonField = field.Name}fmt.Printf("Name: %s, type: %s, json: %s, value: %v\n", field.Name, field.Type, jsonField, v.Field(i))}
}
利用tag修改结构体的某些值
例如,结构体tag中有big的标签,就将值大写
package mainimport ("fmt""reflect""strings"
)type Student struct {Name string `big:"name"`Addr string
}func main() {s := Student{Name: "os",Addr: "bj",}t := reflect.TypeOf(s)v := reflect.ValueOf(&s).Elem()for i := 0; i < t.NumField(); i++ {field := t.Field(i)bigField := field.Tag.Get("big")// 判断类型是不是字符串if field.Type.Kind() != reflect.String {continue}if bigField == "" {continue}// 修改值valueFiled := v.Field(i)valueFiled.SetString(strings.ToTitle(valueFiled.String()))}fmt.Println(s)
}
调用结构体方法
如果结构体有call这个名字的方法,就执行它
package mainimport ("fmt""reflect"
)type Student struct {Name stringAge int
}func (Student) Look(name string) {fmt.Println("look name:", name)
}func (Student) See(name string) {fmt.Println("see name:", name)
}func main() {s := Student{Name: "os",Age: 21,}t := reflect.TypeOf(s)v := reflect.ValueOf(s)for i := 0; i < t.NumMethod(); i++ {methodType := t.Method(i)fmt.Println(methodType.Name, methodType.Type)if methodType.Name != "See" {continue}methodValue := v.Method(i)methodValue.Call([]reflect.Value{reflect.ValueOf("lee"), // 注意这里的类型})}
}
orm的一个小案例
package mainimport ("errors""fmt""reflect""strings"
)type Student struct {Name string `oslee-orm:"name"`Age int `oslee-orm:"age"`
}type UserInfo struct {Id int `oslee-orm:"id"`Name string `oslee-orm:"name"`Age int `oslee-orm:"age"`
}// sql, err := Find(Student{}, "name = ? and age = ?", "os_lee", 18)
func Find(obj any, query ...any) (sql string, err error) {// Find(Student, "name = ?", "os")// 希望能够生成 select name, age from where name = 'os't := reflect.TypeOf(obj)//v := reflect.ValueOf(obj)// 首先得是结构体对吧if t.Kind() != reflect.Struct {err = errors.New("非结构体")return}// 拿全部字段// 拼接条件// 第二个参数,中的问号,就决定后面还能接多少参数var where stringif len(query) > 0 {// 有第二个参数,校验第二个参数中的?个数,是不是和后面的个数一样q := query[0] // 理论上还要校验第二个参数的类型if strings.Count(q.(string), "?")+1 != len(query) {err = errors.New("参数个数不对")return}// 拼接where语句// 将?号带入后面的参数for _, a := range query[1:] {// 替换q// 这里要判断a的类型at := reflect.TypeOf(a)switch at.Kind() {case reflect.Int:q = strings.Replace(q.(string), "?", fmt.Sprintf("%d", a.(int)), 1)case reflect.String:q = strings.Replace(q.(string), "?", fmt.Sprintf("'%s'", a.(string)), 1)}}where += "where " + q.(string)}// 如果没有第二个参数,就是查全部// 拼接select// 拿所有字段,取oslee-orm对应的值var columns []stringfor i := 0; i < t.NumField(); i++ {field := t.Field(i)f := field.Tag.Get("oslee-orm")// 不考虑是空的情况columns = append(columns, f)}// 结构体的小写名字+s做表名name := strings.ToLower(t.Name()) + "s"// 拼接最后的sqlsql = fmt.Sprintf("select %s from %s %s", strings.Join(columns, ","), name, where)return
}func main() {sql, err := Find(Student{}, "name = ? and age = ?", "os_lee", 18)fmt.Println(sql, err) // select name,age from students where name = 'os_lee' and age = 18sql, err = Find(UserInfo{}, "id = ?", 1)fmt.Println(sql, err) // select id,name,age from userinfos where id = 1
}
对反射的一些建议
如果是写一下框架,偏底层工具类的操作
不用反射确实不太好写,但是如果是在业务上,大量使用反射就不太合适了
因为反射的性能没有正常代码高,会慢个一到两个数量级
使用反射可读性也不太好,并且也不能在编译期间发生错误
三、网络编程
socket编程
参考:5.网络编程-socker(golang版)-CSDN博客
websocket编程
参考:4.网络编程-websocket(golang)-CSDN博客
四、部署
go项目的部署特别简单,编写完成之后,只需要执行go build即可打包为可执行文件
注意,这个操作是不同平台不一样的
windows下打包就是exe文件,linux下打包就是二进制文件
打包命令
go build
打当前目录下的main包,注意,只能有一个main函数的包
go build xxx.go
打当前目录下,xxx.go的包,这个包必须得是一个main包,不然没有效果
go build -o main.exe xxx.go
强制对输出的文件进行重命名
-o参数必须得在文件的前面
交叉编译
什么是交叉编译呢,就是在windows上,我开发的go程序,我也能打包为linux上的可执行程序
例如在windows平台,打linux的包
注意,执行set这个命令,一定得要是在cmd的命令行下,powershell是无效的
set CGO_ENABLED=0
set GOOS=linux
set GOARCH=amd64
go build -o main main.go
CGO_ENABLED : CGO 表示 golang 中的工具,CGO_ENABLED=0 表示 CGO 禁用,交叉编译中不能使用 CGO GOOS : 环境变量用于指定目标操作系统,mac 对应 darwin,linux 对应 linux,windows 对应 windows ,还有其它的 freebsd、android 等
GOARCH
:环境变量用于指定处理器的类型,386 也称x86
对应 32位操作系统、amd64
也称 x64 对应 64 位操作系统,arm
这种架构一般用于嵌入式开发。比如Android
,iOS
,Win mobile
等为了方便呢,可以在项目的根目录下写一个bat文件
这样就能快速构建了
然后放到linux服务器下,设置文件权限就可以直接运行了
chmod +x main
./main
再次注意啊,以后打包web项目的时候,配置文件和静态文件等这些非go程序,是要一起复制到目标服务器里面的
参考:Go 学习笔记(37)— 标准命令行工具(go build 跨平台编译、交叉编译、go clean、go run、go fmt、go install、go get、go vet)-CSDN博客
相关文章:
Golang教程六(单元测试,反射,网络编程,部署)
目录 一、单元测试 单元测试 子测试 TestMain 二、反射 类型判断 通过反射获取值 通过反射修改值 结构体反射 利用tag修改结构体的某些值 调用结构体方法 orm的一个小案例 对反射的一些建议 三、网络编程 socket编程 websocket编程 四、部署 打包命令 交叉编译…...

mybatis进阶篇-执行CRUD操作-typeAliases别名-接口绑定
目录结构 1.创建数据表(book) # 创建book表 create table book(id int auto_increment primary key,name varchar(255) ,price double ,num int );2.mybatis.xml配置文件 <?xml version"1.0" encoding"UTF-8" ?> <!DOC…...
C#面:泛型的主要约束和次要约束是什么
在 C# 中,泛型的约束是用来限制泛型类型参数的行为和能力的。 主要约束和次要约束是两种不同的约束方式。 主要约束(Primary Constraint): 主要约束指定了泛型类型参数必须满足的最基本的条件,它可以是一个类、一个接…...
Java使用documents4j将word和excel转pdf
pom.xml添加documents4j依赖 <!-- documents4j --> <dependency><groupId>com.documents4j</groupId><artifactId>documents4j-local</artifactId><version>1.0.3</version> </dependency> <!-- documents4j 转 wor…...
使用策略模式实现 Spring 分布式和单机限流
我们可以使用策略模式来统一单机限流和分布式限流的实现,提高代码的可扩展性和可维护性。 思路是定义一个 RateLimitStrategy 接口,并分别实现单机限流策略 LocalRateLimitStrategy 和分布式限流策略 DistributedRateLimitStrategy。在 AOP 切面中,根据配置决定使用哪种限流策…...
@CrossOrigin注解解决跨域问题
文章目录 一、什么是跨域二、CrossOrigin注解是干什么用的三、用法 一、什么是跨域 跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。 所谓同源是指,域名,协议&…...

【力扣】45. 跳跃游戏 II
Problem: 45. 跳跃游戏 II 文章目录 问题思路复杂度Code 问题 思路 核心思路,例如nums[i]5,那么最远能跳五步; //那么在这接下来1-5范围内,哪个能让我跳的最远,这个最远指的是 -------------------------------------…...
【Python基础】19.eval函数的使用
eval函数 eval()将字符串转变为有效的表达式来求值并返回对应的结果 基础数据计算 In [1]: eval("1 1") Out[1]: 2字符串重复 In [2]: eval (" * * 10") Out[2]: **********字符串转为列表 In [3]: type(eval("[1,2,3,4,5]")) Out[3]: lis…...

对装饰器模式的理解
目录 一、场景二、面对场景中的新需求,我们怎么办?1、暴力法:直接修改原有的代码。2、子类继承法:既然要增强行为,那我搞一个子类,覆写不就完事了?3、装饰器模式 三、对装饰器模式的思考1、从代…...
在替换微软AD的CA证书服务AD CS前,要先做哪些准备工作?
AD CS是什么 关于这个问题,有几个概念需要先弄明白:PKI、CA、数字证书。 PKI(Public Key Infrastructure,公钥基础设施)是提供公钥加密和数字签名服务的系统或平台,实现基于公钥密码体制的密钥和证书的产生…...
Java中的System
文章目录 概要小结 概要 在Java中,System类提供了一些静态方法来实现与系统相关的操作。以下是System类中常用的方法及其含义: System.currentTimeMillis():返回当前时间(以毫秒为单位)自1970年1月1日00:00:00 GMT以来…...
Mybites一对多collection
Goods实体属性: private List<GoodsImg> goodsImgList; private String id; private String name; GoodsImg实体属性: private String id; private String fid; private String imgpath; …...

基于springboot实现图书进销存管理系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现图书进销存管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了图书进销存管理系统的开发全过程。通过分析图书进销存管理系统管理的不足,创建了一个计算机管理图书进销…...

敏捷开发:想要快速交付就必须舍弃产品质量?
随着敏捷的推广与应用,如今已经成为了最有效的团队级别的方法论,越来越多的软件和 IT 团队正在采用敏捷,但是你在敏捷吗? 自从那一群充满影响力的软件从业者聚集在一起并发布了《敏捷宣言》以来,已经过去了 23 年。敏…...
SNMP-详解指南
目录 SNMP介绍 SNMP的工作机制轮询 SNMP的MIB(管理信息库) SNMP是基于UDP协议 SNMP介绍 SNMP(Simple Network Management Protocol,简单网络管理协议)是一种广泛应用于互联网上的网络管理协议。它提供了一种标准化…...

vue-router 原理【详解】hash模式 vs H5 history 模式
hash 模式 【推荐】 路由效果 在不刷新页面的前提下,根据 URL 中的 hash 值,渲染对应的页面 http://test.com/#/login 登录页http://test.com/#/index 首页 核心API – window.onhashchange 监听 hash 的变化,触发视图更新 window.onhas…...

WebGl/Three 粒子系统 人物破碎及还原运动
粒子 首先,加载模型,这是万千粒子的前身,模型对象由很多面构成,这些面又是由各个点构成的,所以可以将模型的几何体对象geometry赋给粒子对象,粒子物体用Points方式渲染 bloader.load("obj/female02/Fe…...
华为OD-C卷-分披萨[100分]
题目描述 "吃货"和"馋嘴"两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数个小块。但是粗心的服务员将披萨切成了每块大小都完全不同奇数块,且肉眼能分辨出大小。 由于两人都想吃到最多的披萨,他们商量了一个他们认…...
uniapp 中video标签视频禁止快,拖拽快进
废话不多说,直接上代码 <video id"myVideo" :src"sectionInfo.type_config.video_url" timeupdate"bindtimeupdate"></video> <script>export default {data() {return {historyTime: 0,}},methods:{// 监听播放进…...

网页端HTML使用MQTTJs订阅RabbitMQ数据
最近在做一个公司的日志组件时有一个问题难住了我。今天问题终于解决了。由于在解决问题中,在网上也查了很多资料都没有一个完整的实例可以参考。所以本着无私分享的目的记录一下完整的解决过程和实例。 需求:做一个统一日志系统可以查看日志列表和一个可…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...

springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...