go学习之json和单元测试知识

文章目录

一、json以及序列化

1.概述

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。key-val

JSON是2001年开始推广使用的数据格式,目前已成为主流的数据格式

JSON易于机器解析和生成,并有效地提升网络传输效率,通常程序在网络传输时会先将数据(结构体、map等)序列化成json字符串时,在反序列化恢复成原来的数据类型(结构体、map等)。这种方式已然成为各个语言的标准

2.json应用场景图

3.json数据格式说明

在JS语言中,一切都是对象。因此,任何支持的类型都可以通过JSON来表示,例如字符串、数字、对象、数组等

JSON键值对是用来保存 数据的一种方式

键/值对组合中的键名写在前面并引用双引号""包裹,使用冒号:分隔,然后紧接着值:

{"key1":val1,"key2":val2,"key3":val3,"key4":\[val4,val5\]}, {"key1":val1,"key2":val2,"key3":val3,"key4":\[val4,val5\]}

比如:

json 复制代码
{"firstName": "Json"}
比如:
{"name":"tom","age":18,"address":["北京","上海"]}
比如:
[{"name":"tom","age":18,"address":["北京","上海"]},
{"name":"tom","age":18,"address":["北京","上海"]}]

任何数据类型都可以转换为json格式

json在线验证网站www.json.cn

4.json的序列化

1)介绍

json序列化是指,将有key-value结构的数据类型(比如结构体、map、切片)序列化成json字符串的操作

2)应用案例

这里我们介绍一下结构体、map和切片的序列化,其他数据类型的序列化类似

go 复制代码
package main
import (
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string
	Age int
	Birthday string
	Sal float64
	Skill string
}

//将结构体序列化的演示
func testStruct() {
	//演示
	var monster = Monster{
		Name : "牛魔王",
		Age : 500,
		Birthday : "2011-11-11",
		Sal : 8000.0,
		Skill : "牛魔拳",
	}

	//将moster进行序列化
	data, err := json.Marshal(&monster)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	//输出序列化后的结果
	fmt.Printf("monster序列化后=%v\n",string(data))
}

//将Map序列化的演示
func testMap(){
	//定义一个Map
    var a map[string]interface{}
	//使用map,需要make
	a = make(map[string]interface{})
	a["name"] = "红孩儿"
	a["age"] = 30
	a["address"] = "洪崖洞"

	//将a这个map进行序列化
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	//输出序列化后的结果
	fmt.Printf("a map序列化后=%v\n",string(data))
}

//演示对切片进行序列化
func testSlice() {
	var slice []map[string]interface{}
	var m1 map[string]interface{}
	//使用map前,需要先make
	m1 = make(map[string]interface{})
	m1["name"] = "jack"
	m1["age"] = 30
	m1["address"] = "北京"
	slice = append(slice,m1)

	var m2 map[string]interface{}
	//使用map前,需要先make
	m2 = make(map[string]interface{})
	m2["name"] = "tom"
	m2["age"] = 20
	m2["address"] = [2]string{"墨西哥","夏威夷"}
	slice = append(slice,m2)

	//将切片进行序列化操作
	data, err := json.Marshal(slice)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	//输出序列化后的结果
	fmt.Printf("slice序列化后=%v\n",string(data))

}

//对基本数据类型进行序列化操作
func testFloat64() {
	var num1 float64 = 2345.67

	//对num1进行序列化
	data, err := json.Marshal(num1)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	//输出序列化后的结果
	fmt.Printf("num1序列化后=%v\n",string(data))
}
func main() {
	//演示将结构体,map,切片进行序列化
	testStruct()
//输出结果如下:monster序列化后={"Name":"牛魔王","Age":500,"Birthday":"2011-11-11","Sal":8000,"Skill":"牛魔拳"}	
	testMap()
//输出结果如下:a map序列化后={"address":"洪崖洞","age":30,"name":"红孩儿"}
    testSlice()
//输出结果如下:slice序列化后=[{"address":"北京","age":30,"name":"jack"},{"address":"墨西哥","age":20,"name":"tom"}]
    testFloat64() //num1序列化后=2345.67,将它变为字符串
	//将基本数据类型进行序列化意义不大
}

注意事项,对于结构体的序列化,如果我们希望序列化后的key的名字,由我们自己重新制定,那么可以给struct指定一个tag标签

go 复制代码
//定义一个结构体
type Monster struct {
	Name string `json:"monster_name"`//运用反射机制
	Age int `json:"monster_age"`
	Birthday string
	Sal float64
	Skill string
}
//这样做可以指定key值

序列化后:monster序列化后={"monster_name":"牛魔王","monster_age":500,"Birthday":"2011-11-11","Sal":8000,"Skill":"牛魔拳"}

5.json的反序列化

1)介绍

json反序列化是指,将json字符串反序列化成对应的数据类型(比如结构体、map、切片)的操作

2)应用案例

这里我们介绍一下将jason字符串反序列化成结构体、map和切片

代码演示

go 复制代码
package main
import (
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string 
	Age int
	Birthday string
	Sal float64
	Skill string
}
//演示将json字符串。反序列化成struct
func umarshalstruct() {
	//说明str 在项目开发中,是通过网络传输获取到的...或者通过读取文件得到
	str := "{\"Name\":\"牛魔王\",\"Age\":500,\"Birthday\":\"2011-11-11\",\"Sal\":8000,\"Skill\":\"牛魔拳\"}"
    //定义一个Monster实例
	var monster Monster

	err := json.Unmarshal([]byte(str),&monster)
    if err != nil {
		fmt.Printf("unmarshal err=%v\n",err)
	}
	fmt.Printf("反序列化后 monster=%v\n",monster)
	//单独取出结构体中的一个字段
	fmt.Printf("反序列化后 monster.Name=%v\n",monster.Name)
}

//演示将jason字符串反射成map
func unmarshalMap() {
	str := "{\"address\":\"洪崖洞\",\"age\":30,\"name\":\"红孩儿\"}"
    
	//定义一个map
	var a map[string]interface{}
	//反序列化就不需要进行make了因为他会自动进行make操作
    
	//反序列化
	err := json.Unmarshal([]byte(str),&a)
    if err != nil {
		fmt.Printf("unmarshal err=%v\n",err)
	}
	fmt.Printf("反序列化后 a=%v\n",a)
	//单独取出结构体中的一个字段
	// fmt.Printf("反序列化后 monster.Name=%v\n",monster.Name)


}
//演示将json串反序列化文slice
func unmarshalSlice() {
	str := "[{\"address\":\"北京\",\"age\":30,\"name\":\"jack\"}," +
	"{\"address\":[\"墨西哥\",\"夏威夷\"],\"age\":20,\"name\":\"tom\"}]"
	//定义一个切片
	var slice []map[string]interface{}

	//反序列化
	err := json.Unmarshal([]byte(str),&slice)
    if err != nil {
		fmt.Printf("unmarshal err=%v\n",err)
	}
	fmt.Printf("反序列化后 slice=%v\n",slice)

}




func main() {
	umarshalstruct()
//输出的结果为:反序列化后 monster={牛魔王 500 2011-11-11 8000 牛魔拳}
    unmarshalMap()
//输出结果为:反序列化后 a=map[address:洪崖洞 age:30 name:红孩儿]
	unmarshalSlice()
//反序列化后 slice=[map[address:北京 age:30 name:jack] map[address:[墨西哥 夏威夷] age:20 name:tom]]	

}

对上面代码的注意事项

  • 在反序列化一个json字符串时,要确保反序列化后的数据类型和原来序列化前的数据类型一致

  • 如果json字符串是通过程序获取获取到的,则不需要对 ""进行转义处理",因为转义处理已经包含在内部了

二、单元测试

1.引子

先看一个需求,怎样确定他运行的结果是正确的

go 复制代码
func addUpper (n int) int {
	res := 0
	for i :=1;i <=n;i++ {
		res +=i
	}
	return res
}

传统的方法解决:

在main函数中,调用addUpper函数,看看实际输出的结果是否与你预期的结果一致,如果一致,则说明函数正确。否则函数有错误,然后修改错误

go 复制代码
package main
import (
	"fmt"
)
//一个被测试函数
func addUpper (n int) int {
	res := 0
	for i :=1;i <=n;i++ {
		res +=i
	}
	return res
}
func main() {
	//传统的测试方法,就是在main函数中使用看看结果是否正确
    res :=addUpper(10)
	if res != 55 {
		fmt.Printf("adUpper错误,返回值=%v 期望值=%v\n",res,55)
	} else {
        fmt.Printf("adUpper正确,返回值=%v 期望值=%v\n",res,55)
	}
	
}

传统方法的缺点分析

  • 不方便,我们需要在main函数中去调用,这样就需要去修改main函数,如果现在项目正在运行,就可能去停止项目。
  • 不利于管理,因为当我们测试多个函数或者多个模块时,都需要写在main函数中,不利于我们的管理和清晰我们的思路
  • 引出单元测试。->testing测试框架,可以很好的解决问题

2.单元测试-基本介绍

go语言中自带一个轻量记得测试框架testing和自带的go test命令来完成单元测试和性能测试,testing框架和其他语言中的测试框架类似,可以基于该框架写相应的压力测试用例。通过单元测试,可以解决以下问题

1)确保每个函数是可运行的,并且运行结果是正确的

2)确保写出来的代码性能是好的

3)单元测试及时的发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决,而性能测试的重点在于发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定

使用go的单元测试,对addUpper和sub函数进行测试

注意:测试时,可能需要暂时退出360(因为360可能认为生成的测试用例的程序是木马)

3.代码实现

go 复制代码
package main

//一个被测试函数
func AddUpper (n int) int {
	res := 0
	for i :=1;i <=n;i++ {
		res +=i
	}
	return res
}

//求两个数的差
func getSub(n1 int,n2 int) int {
	return n1 - n2
}

cal_test.go

go 复制代码
package main
import (
	_"fmt"
	"testing" //引入go的testing框架包
)

//编写测试用例,去测试,去测试addUpper函数是否正确   、
func TestAddUpper(t *testing.T) {
  
	//调用
	res := AddUpper(10)
	if res != 55 {
		//fmt.Println("AddUpper(10)执行错误,期望值=%v实际值=%v\n",55,res)
		t.Fatalf("AddUpper(10)执行错误,期望值=%v实际值=%v\n",55,res)
	}

	//如果正确,输出日志
	t.Logf("AddUpper(10)执行正确...")
}                                                                                                                                                                                                                                                         

sub_test.go

go 复制代码
package main
import (
	_"fmt"
	"testing" //引入go的testing框架包
)

//编写测试用例,去测试,去测试sub函数是否正确   、
func TestGetSub(t *testing.T) {
  
	//调用
	res := getSub(10,3)
	if res != 7 {
		
		t.Fatalf("getSub(10)执行错误,期望值=%v实际值=%v\n",7,res)
	}

	//如果正确,输出日志
	t.Logf("getSub(10)执行正确...")
}                                                                                                                                                           

在cmd中执行go test -v就可以对此函数进行测试操作了

单元测试的运行原理

4.单元测试的细节说明

  • 测试用例文件名必须以_test.go结尾,比如cal_test.go,cal不是固定的

  • 测试用例函数必须以Test开头,一般来说就是Test_被测试的函数名,比如TestAddUpper.

  • TestAddUpper(t testing.T)的形参类型必须是testing.T

  • 一个测试用例文件中,可以有多个测试用例函数,比如TestUpper.TestSub

  • 运行测试用例的指令为

    1. cmd > go test [如果运行正确,无日志,错误时,会输出日志]
    2. cmd>go test -v [运行正确或者错误,都输出日志]
  • 当出现错误时,可以用t.Fatalf来格式化输出错误信息,并退出程序

  • t.Logf("")方法可以输出相应的日志

  • 测试用例函数,并没有放在main函数中,也执行了,这就是测试用例的方便之处

  • PASS表示测试用例运行成功,FAIL表示测试用例运行失败

  • 测试单个文件一定要带上被测试的源文件

    go test -v cal.test,go cal.go

  • 测试单个方法

    go test -v -test.run TestAddUpper

  • sd

5.单元测试的综合案例

1)编写一个Monter结构体,字段Name,Age,Skill

2)给Monster绑定方法Store,可以将一个Monster变量(对象),序列化后保存到文件中

3)给Monster绑定方法ReStore,可以将一个序列化的Monster,从文件中读取,并反序列化为Monster对象

4)编程测试用例文件store_go编写测试用例函数TestStore和TestRestore进行测试

monster.go

go 复制代码
package monster
import (
	"encoding/json"
	"io/ioutil"
	"fmt"
)
type Monster struct {
	Name string
	Age int
	Skill string
}

//给Monster绑定方法Store,可以将一个Monster变量(对象),序列化后保存到文件中
func (this *Monster) Store() bool{

	//先序列化
	data, err := json.Marshal(this)
	if err != nil {
		fmt.Println("marshal err = ", err)
		return false
	}
	//保存到文件
		filePath := "D:/test/test02/monster.ser"
		err = ioutil.WriteFile(filePath, data,0666)
		if err != nil {
			fmt.Println("write file  err = ", err)
			return false
	}
	return true
	//保存到文件中
}





//给Monster绑定方法ReStore,可以将一个序列化的Monster,从文件中读取,
// 并反序列化为Monster对象
func (this *Monster) ReStore() bool {

	//1.先从文件中读取序列化字符串
	filePath := "D:/test/test02/monster.ser"
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		fmt.Println("Read file  err = ", err)
		return false
	}

	//2.使用读取到的data []byte,对反序列化
	err = json.Unmarshal(data,this)
	if err != nil {
        fmt.Println("Unmarshal  err = ", err)
		return false
	}
	return true
}

monster_test.go

go 复制代码
package monster
import (
	"testing"
)
//测试用例,测试Store方法
func TestStore(t *testing.T) {

	//先创建一个Monster实例
	monster := &Monster {
		Name : "红孩儿",
		Age : 10,
		Skill : "吐火",
	}
	res := monster.Store()
	if !res {
		t.Fatalf("monster.Store()错误,希望为=%v 实际为=%v",true,res)
	}
	t.Logf("monster.Store()测试成功")

}

func TestReStore(t *testing.T) {
	//创建一个Monster实例,不需要指定字段的值
	var monster = &Monster{}
	res := monster.ReStore()
	if !res {
		t.Fatalf("monster.ReStore()错误,希望为=%v 实际为=%v",true,res)
	}

	//进一步判断
	if monster.Name != "红孩儿" {
		t.Fatalf("monster.ReStore()错误,希望为=%v 实际为=%v",true,monster.Name)

	}
	t.Logf("monster.ReStore()测试成功")

	}	

cmd运行

复制代码
D:\myfile\GO\project\src\go_code\TestUnit\demo2>go test -v -test.run TestReStore
=== RUN   TestReStore
--- PASS: TestReStore (0.00s)
        moster_test.go:35: monster.ReStore()测试成功
PASS
ok      go_code/TestUnit/demo2  0.191s

将测试文件中改一下

复制代码
D:\myfile\GO\project\src\go_code\TestUnit\demo2>go test -v -test.run TestReStore
=== RUN   TestReStore
--- FAIL: TestReStore (0.00s)
        moster_test.go:32: monster.ReStore()错误,希望为=true 实际为=红孩儿~
FAIL
exit status 1
FAIL    go_code/TestUnit/demo2  0.181s

t.Logf("monster.ReStore()测试成功")

}	
复制代码
相关推荐
花酒锄作田5 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode