Golang教程六(单元测试,反射,网络编程,部署)

目录

一、单元测试

单元测试 

 子测试

 TestMain

二、反射

类型判断

通过反射获取值

通过反射修改值

结构体反射

利用tag修改结构体的某些值

调用结构体方法

orm的一个小案例

对反射的一些建议

三、网络编程

socket编程

websocket编程

四、部署

打包命令

交叉编译


一、单元测试

Go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言的测试框架相似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。通过单元测试,可以解决:

  1. 确保每个函数是可运行,并且运行结果是正确的
  2. 确保写出来的代码性能是好的
  3. 单元测试能及时的发现程序设计或实现的逻辑错误,使问题暴露,便于问题的定位解决,而性能测试的重点在于发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定

Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go 结尾

注意点:

  1. 测试用例文件名必须以_test.go结尾
  2. 测试用例函数必须以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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/815077.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[攻防世界]Reversing-x64Elf-100

1.查壳 无壳&#xff0c;ELF文件 2.用IDA64打开 找到关键部分 这里有坑&#xff0c;看清楚v3是长度为3数组&#xff0c;里面放三个字符串 3.脚本解密 v1"Dufhbmf" v2"pGimos" v3"ewUglpt" v4[v1,v2,v3] a1[0,0,0,0,0,0,0,0,0,0,0,0] for i …

人工智能研究生前置知识—科学计算库numpy

人工智能研究生前置知识—科学计算库numpy numpy是python中做科学计算的基础库&#xff0c;对数组进行操作 整个numpy的操作和使用比较简单因此可以通过案例的学习掌握基本的用法在之后的学习中不断的进行熟悉和补充 创建数组&#xff08;矩阵 &#xff09; 创建的ndarray数组…

使用cmake进行打包,包含可执行程序和动态依赖库

平常代码开发中&#xff0c;有时候需要将写的程序打包成压缩包放到目标设备上进行运行测试。用CMake管理工程&#xff0c;实现使用make -jnproc package指令可以将工程进行打包&#xff0c;可执行文件存储在bin文件夹中&#xff0c;依赖库存储在lib文件夹中。 示例 1.工程目录结…

谷歌翻译接口-国内使用在线翻译API

对于翻译要求比较高的同学都知道Google翻译比现有市面上的翻译软件优势在哪里。下面来浅浅介绍一下其优点&#xff1a; 1、支持超过100种不同语言之间的互译&#xff0c;覆盖了全球主要语言。 2、基于机器学习技术&#xff0c;可根据用户大量的翻译数据不断优化和提升翻译的准确…

算法打卡day41|动态规划篇09| Leetcode198.打家劫舍、213.打家劫舍II、337.打家劫舍 III

算法题 Leetcode 198.打家劫舍 题目链接:198.打家劫舍 大佬视频讲解&#xff1a;198.打家劫舍视频讲解 个人思路 偷还是不偷&#xff0c;这取决于前一个和前两个房是否被偷了&#xff0c;这种存在依赖关系的题目可以用动态规划解决。 解法 动态规划 动规五部曲&#xff1…

B端系统:控制台图表的十大常见类型,附精美案例

大家伙&#xff0c;我是大千UI工场&#xff0c;专注UI分享和项目接单&#xff0c;本期带来控制台图表的常见类型&#xff0c;欢迎大家关注、互动交流。 B端系统控制台的图表类型有很多种&#xff0c;常见的包括&#xff1a; 折线图&#xff1a;用于显示随时间变化的数据趋势&a…

Spring Boot 学习(3)——Spring Initializr 创建项目问题解决

产生问题的原因&#xff0c;各种的版本都较老&#xff0c;所以导致出现问题。目前暂未打到合适的教程&#xff0c;按老教程学起来先。 小白瞎学&#xff0c;大神勿喷&#xff01; 再次强调环境&#xff1a;maven 3.3.9、jdk 1.8、idea 2017、Spring 4.3.13、Spring Boot 1.5.…

【动态规划 区间dp 位运算】100259. 划分数组得到最小的值之和

本文涉及知识点 动态规划 区间dp 位运算 LeetCode100259. 划分数组得到最小的值之和 给你两个数组 nums 和 andValues&#xff0c;长度分别为 n 和 m。 数组的 值 等于该数组的 最后一个 元素。 你需要将 nums 划分为 m 个 不相交的连续 子数组&#xff0c;对于第 ith 个子数…

设计者模式之中介者模式(下)

3&#xff09;中介者与同事类的扩展 1.结构图 新增了具体同事类Label和具体中介者类SubConcreteMediator。 2.代码实现 //文本标签类&#xff1a;具体同事类 public class Label extends Component {public void update() {System.out.println("文本标签内容改变&#…

【蓝桥杯-牛客冲刺题单】

蓝桥杯-牛客冲刺题单 前言&#xff1a;牛牛的早八光速签到牛牛的身高A加B,A模B四舍五入小红的环形字符串牛牛的说谎机器传送阵优美的序列小d和超级泡泡堂qsgg and Subarray结语 前言&#xff1a; 蓝桥杯前一天除了看看模板和做过的题&#xff0c;也可以限时做一下中等偏下的题…

linux中screen命令及Tmux的使用

linux中screen命令及Tmux的使用.md Screen使用一、大概使用的过程示例 Tmux使用安装基本用法会话管理窗口管理 Screen使用 本文内容摘自原文https://blog.csdn.net/weixin_43570155/article/details/121845415 使用 screen 可以帮助你在后台运行程序并保持持久性。下面是一些…

鉴权设计(一)———— 登录验证

1、概述 网站系统出于安全性的考虑会对用户进行两个层面的校验&#xff1a;身份认证以及权限认证。这两个认证可以保证只有特定的用户才能访问特定的数据的需求。 本文先实现一个基于jwt拦截器redis注解实现的简单登录验证功能。 2、设计思路 jwt用于签发token。 拦截器用于拦…

南京观海微电子---二极管钳位电路

在选择电阻器和电容器时&#xff0c;您必须注意电容器的放电时间&#xff0c;因为它会保持波形的时间段。它必须比时间段的一半大得多&#xff0c;以便电容器放电缓慢。电解电容器不应用于钳位电路&#xff0c;因为它们的充电和放电速度很慢。放电时间&#xff08;&#xff09;…

容器镜像进阶

Dockerfile 编写注意事项&#xff1a; 选择合适的基础镜像&#xff0c;没必要追求镜像的绝对大小。 alpine镜像不推荐&#xff0c;尤其是编译型业务&#xff0c;因为alpine镜像内置的musl libc库与标准的glibc不一样。 如果就是想使用alpine镜像&#xff0c;推荐多阶段构建&am…

2023-2024年科技行业主要变化趋势梳理与总结

一、主要大额并购案例并购原因 &#xff08;一&#xff09;产品线补充与业务市场拓展&#xff08;如VMware、Activision Blizzard并购案&#xff09; &#xff08;二&#xff09;技术整合&#xff08;如Spunk并购案&#xff09;、 &#xff08;三&#xff09;优化运营以期溢…

分类预测 | Matlab实现PSO-LSSVM粒子群算法优化最小二乘支持向量机数据分类预测

分类预测 | Matlab实现PSO-LSSVM粒子群算法优化最小二乘支持向量机数据分类预测 目录 分类预测 | Matlab实现PSO-LSSVM粒子群算法优化最小二乘支持向量机数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现PSO-LSSVM粒子群算法优化最小二乘支持向量…

C++ 红黑树模拟实现

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C知识分享⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 前言 前面我们实现了AVL树&#xff0c;发明AVL树…

AI大模型探索之路-实战篇:基于CVP架构-企业级知识库实战落地

目录 前言 一、概述 二、本地知识库需求分析 1. 知识库场景分析 2. 知识库应用特点 3. 知识库核心功能 三、本地知识库架构设计 1. RAG架构分析 2. 大模型方案选型 3. 应用技术架构选型 4. 向量数据库选型 5. 模型选型 三、本地知识库RAG评估 四、本地知识库代码落地 1. 文件…

一文了解LangChain的记忆模块(理论实战+细节)

大多数LLM应用程序都有一个会话接口。会话的一个重要组成部分是能够参考会话早期的信息&#xff08;上文信息&#xff09;。这种存储过去互动信息的能力就称为“记忆&#xff08;Memory&#xff09;”。LangChain提供了许多用于向系统添加Memory的封装。 目前 LangChain 中大多…

OJ 连续数的和 球弹跳高度的计算【C判断是否为完全平方数】【格式输出%g输出全部小数部分】

连续数的和 判断是否为完全平方数有两种方法 1.遍历所有小于该数的整数&#xff0c;有一个满足平方与该数相等&#xff0c;则是完全平方数 2.用sqrt()或pow()函数对该数开方&#xff0c;取整&#xff08;舍去小数部分&#xff09;&#xff0c;再平方&#xff0c;与该数相等则…