目錄
- 檔案操作
- 輸入流和輸出流
- 打開檔案和關閉檔案
- 使用的函式和方法
- 案例演示
- 讀檔案操作應用案例
- 寫檔案操作應用案例
- 判斷檔案是否存在
- 檔案編程應用實體
- 拷貝檔案
- 統計英文、數字、空格和其它字符數量
- 命令列引數
- 舉例說明
- flag包用來決議命令列引數
- Json
- Json基本介紹
- 應用場景
- Json資料格式說明
- Jsnon資料在線決議
- Json的序列化
- 應用案例
- Json的反序列化
- 應用案例
- Json基本介紹
- 單元測驗
- 先看一個需求
- 傳統的方法
- 傳統方法的缺點分析
- 單元測驗
- 基本介紹
- 快速入門
- 單元測驗快速入門總結
- 綜合案例
- 單元測驗綜合案例要求:
- 代碼區
檔案操作
檔案是資料源(保存資料的地方)的一種,比如經常使用的word檔案,txt檔案,excel檔案...都是檔案,檔案最主要的作用就是保存資料,它即可以保存一張圖片,也可以保持視頻,聲音...
輸入流和輸出流
檔案在程式中是以流的形式來操作的

流:資料在資料源(檔案)和程式(記憶體)之間經歷的路徑
輸入流:資料從資料源(檔案)到程式(記憶體)的路徑
輸出流:資料從程式(記憶體)到資料源(檔案)的路徑
os.File封裝所有檔案相關操作,File是一個結構體
? 后面操作檔案,會經常使用到os.File結構體
打開檔案和關閉檔案
使用的函式和方法


案例演示
import (
"fmt"
"os"
)
func main() {
//打開檔案
//概念說明:file的叫法
//1. file 叫 file物件
//2. file 叫 file指標
//3. file 叫 file檔案句柄
file, err := os.Open("e:/test.txt")
if err != nil {
fmt.Println("Open file err = ", err)
}
//輸出檔案,看看檔案是什么,看出file就是一個指標 *Filr
fmt.Printf("file = %v", file) //file = &{0xc000070780}
//關閉
err = file.Close()
if err != nil {
fmt.Println("Close file err = ", err)
}
}
讀檔案操作應用案例
- 讀取檔案的內容并顯示在終端(帶緩沖區的方式),使用os.Open,file.Close,bufio.NewReader(),reader.ReadString函式和方法
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
//打開檔案
//概念說明:file的叫法
//1. file 叫 file物件
//2. file 叫 file指標
//3. file 叫 file檔案句柄
file, err := os.Open("E:/gostudent/src/2020-04-02/utils/utils.go")
if err != nil {
fmt.Println("Open file err = ", err)
}
//當函式退出時,要及時的關閉file
defer file.Close() //要及時關閉file句柄,否則會有記憶體泄漏
//創建一個*Reader , 是帶緩沖的
/*
const (
defaultBufSize = 4096 //默認的緩沖區為4096
)
*/
reader := bufio.NewReader(file)
//回圈讀取檔案的內容
for {
str, err := reader.ReadString('\n') //讀到一個換行就結束
if err == io.EOF { //io.EOF 表示檔案的末尾
break
}
//輸出內容
fmt.Print(str)
}
fmt.Println("檔案讀取結束...")
}
- 讀取檔案的內容并顯示在終端(使用ioutil一次將整個檔案讀入到記憶體中),這種方式適用于檔案不大的情況,相關方法和函式ioutil.ReadFile
import (
"fmt"
"io/ioutil"
)
func main() {
//使用ioutil.ReadFile一次性將檔案讀取到位
file := "E:/gostudent/src/2020-04-02/utils/utils.go"
content, err := ioutil.ReadFile(file)
if err != nil {
fmt.Printf("read file err = %v", err)
}
//把讀取到的內容顯示到終端
//fmt.Printf("%v", content) //[]byte
fmt.Printf("%v", string(content)) // []byte
//這里沒有顯示的Open檔案,因此也不需要顯示的Close檔案
//因為,檔案的Open和Close被封裝到ReadFile函式內部
}
寫檔案操作應用案例
os.OpenFile函式

- 創建一個新檔案,寫入內容:5句 “Hello,zisefeizhu”
import (
"bufio"
"fmt"
"os"
)
func main() {
filePath := "E:/gostudent/src/2020-04-05/abc.txt"
file, err := os.OpenFile(filePath, os.O_CREATE | os.O_WRONLY, 0666)
if err != nil {
fmt.Printf("open file err = %v \n", err)
return
}
//及時關閉file句柄
defer file.Close()
//準備寫入5句: "hello,zisefeizhu"
str := "hello,zisefeizhu\n" // \n表示換行
//寫入時,使用帶快取的*Writer
writer := bufio.NewWriter(file)
for i := 0; i< 5; i++ {
writer.WriteString(str)
}
//因為write是帶快取的,因此在呼叫WriterString方法時
//其實內容是先寫入到快取的,所以需要呼叫Flush方法,將緩沖的資料
//真正寫入到檔案中,否則檔案中會沒有資料!!!
writer.Flush()
}
- 打開一個存在的檔案,將原來的內容覆寫成新的內容10句“你好,紫色飛豬”
import (
"bufio"
"fmt"
"os"
)
func main() {
//2)打開一個存在的檔案,將原來的內容覆寫成新的內容10句“你好,紫色飛豬”
//1. 打開已經存在的檔案E:/gostudent/src/2020-04-05/abc.txt
filePath := "E:/gostudent/src/2020-04-05/abc.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_TRUNC, 0666)
if err != nil {
fmt.Printf("open file err = %v \n", err)
return
}
//及時關閉file句柄
defer file.Close()
//準備寫入10句: "hello,zisefeizhu"
str := "你好,紫色飛豬!\n" // \n表示換行
//寫入時,使用帶快取的*Writer
writer := bufio.NewWriter(file)
for i := 0; i< 10; i++ {
writer.WriteString(str)
}
//因為write是帶快取的,因此在呼叫WriterString方法時
//其實內容是先寫入到快取的,所以需要呼叫Flush方法,將緩沖的資料
//真正寫入到檔案中,否則檔案中會沒有資料!!!
writer.Flush()
}
- 打開一個存在的檔案,在原來的內容追加內容“你好,jingxing”
import (
"bufio"
"fmt"
"os"
)
func main() {
//1. 打開已經存在的檔案E:/gostudent/src/2020-04-05/abc.txt
filePath := "E:/gostudent/src/2020-04-05/abc.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_APPEND, 0666)
if err != nil {
fmt.Printf("open file err = %v \n", err)
return
}
//及時關閉file句柄
defer file.Close()
//追加內容
str := "你好,jingxing\n" // \n表示換行
//寫入時,使用帶快取的*Writer
writer := bufio.NewWriter(file)
for i := 0; i< 10; i++ {
writer.WriteString(str)
}
//因為write是帶快取的,因此在呼叫WriterString方法時
//其實內容是先寫入到快取的,所以需要呼叫Flush方法,將緩沖的資料
//真正寫入到檔案中,否則檔案中會沒有資料!!!
writer.Flush()
}
- 打開一個存在的檔案,將原來的內容讀出顯示在終端,并且追加5句“你好,深圳”
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
//1. 打開已經存在的檔案E:/gostudent/src/2020-04-05/abc.txt
filePath := "E:/gostudent/src/2020-04-05/abc.txt"
file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666)
if err != nil {
fmt.Printf("open file err = %v \n", err)
return
}
//及時關閉file句柄
defer file.Close()
//先讀取原來檔案的內容,并顯示在終端
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF { //如果讀取到檔案的末尾
break
}
//顯示到終端
fmt.Print(str)
}
//追加內容
str := "你好,深圳\n" // \n表示換行
//寫入時,使用帶快取的*Writer
writer := bufio.NewWriter(file)
for i := 0; i< 5; i++ {
writer.WriteString(str)
}
//因為write是帶快取的,因此在呼叫WriterString方法時
//其實內容是先寫入到快取的,所以需要呼叫Flush方法,將緩沖的資料
//真正寫入到檔案中,否則檔案中會沒有資料!!!
writer.Flush()
}
5)撰寫一個程式,將一個檔案的內容,寫入到另外一個檔案,注:這兩個檔案已經存在了
說明:使用ioutil.ReadFile / ioutil.WriteFile完成寫檔案的任務
import (
"fmt"
"io/ioutil"
)
func main() {
//將e:/abc.txt 檔案內容匯入到e:/abc.txt
//1. 首先將 e:/abc.txt 內容讀取到記憶體
//2. 將讀取到的內容寫入d:/abc.txt
file1Path := "E:/gostudent/src/2020-04-05/abc.txt"
file2Path := "D:/abc.txt"
data, err := ioutil.ReadFile(file1Path)
if err != nil {
//說明讀取檔案有錯誤
fmt.Printf("read file err = %v\n", err)
return
}
err = ioutil.WriteFile(file2Path, data, 0666)
if err != nil {
fmt.Printf("write file error = %v \n", err)
}
}
判斷檔案是否存在
Go判斷檔案或檔案夾是否存在的方法為使用os.Stat()函式對回傳的錯誤值進行判斷:
-
如果回傳的錯誤為nil,說明檔案或檔案夾存在
-
如果回傳的錯誤型別使用os.IsNotExist()判斷為true,說明檔案或檔案夾不存在
-
如果回傳的錯誤為其它型別,則不確定是否存在

檔案編程應用實體
拷貝檔案
說明:將一張圖片/電影/mp3拷貝到另一個檔案e:/abc.jpg
func Copy(dst Writer,src Reader)(written int64, err error)
注意:Copy函式是io 包提供的
import (
"bufio"
"fmt"
"io"
"os"
)
//撰寫一個函式,接收兩個檔案路徑 srcFileName dstFileName
func CopyFile(srcFileName string, dstFileName string) (written int64, err error) {
srcFile, err := os.Open(srcFileName)
if err != nil {
fmt.Printf("open file err = %v\n", err)
}
defer srcFile.Close()
//通過srcfile,獲取到Reader
reader := bufio.NewReader(srcFile)
//打開dstFileName
dstFile,err := os.OpenFile(dstFileName, os.O_WRONLY | os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err = %v\n", err)
return
}
//通過dstFile,獲取到Writer
writer := bufio.NewWriter(dstFile)
defer dstFile.Close()
return io.Copy(writer, reader)
}
func main() {
//將d:/abc.jpg 檔案拷貝到e:/abc.jpg
//呼叫CopyFile 完成檔案拷貝
srcFile := "d:/abc.jpeg"
dstFile := "e:/abc.jpg"
_, err := CopyFile(srcFile, dstFile)
if err == nil {
fmt.Printf("拷貝完成\n")
} else {
fmt.Printf("拷貝錯誤 err = %v\n", err)
}
}
統計英文、數字、空格和其它字符數量
import (
"bufio"
"fmt"
"io"
"os"
)
//定義一個結構體,用于保存統計結果
type CharCount struct {
ChCount int //記錄英文個數
NumCount int //記錄數字的個數
SpaceCount int //記錄空格的個數
OtherCount int //記錄其它字符的個數
}
func main() {
//思路:打開一個檔案,創一個Reader
//每讀取一行,就去統計該行有多少個英文、數字、空格和其它字符
//然后將結果保存到一個結構體
fileName := "E:/gostudent/src/2020-04-05/abc.txt"
file,err := os.Open(fileName)
if err != nil {
fmt.Printf("open file err = %v \n", err)
return
}
defer file.Close()
//定義個CharCount實體
var count CharCount
//創建一個Reader
reader := bufio.NewReader(file)
//開始回圈讀取fleName的內容
for {
str, err := reader.ReadString('\n')
if err == io.EOF { //讀到檔案末尾就退出
break
}
//為了兼容中文字符,可以將str轉成[]rune
strChange := []rune(str)
//遍歷str,進行統計
for _,v := range strChange {
switch {
case v >= 'a' && v <= 'z' :
fallthrough //穿透
case v >= 'A' && v <= 'Z' :
count.ChCount++
case v == ' ' || v == '\t' :
count.SpaceCount++
case v >= '0' && v <= '9' :
count.NumCount++
default:
count.OtherCount++
}
}
}
//輸出統計的結果看看是否正確
fmt.Printf("字符的個數為 = %v 數字的個數為 = %v 空格的個數為 = %v 其它字符個數 = %v",
count.ChCount, count.NumCount, count.SpaceCount, count.OtherCount)
}
命令列引數
希望能夠獲取到命令列輸入的各種引數,該如何處理?
os.Args是一個string的切片,用來存盤所有的命令列引數
舉例說明
import (
"fmt"
"os"
)
func main() {
fmt.Println("命令列的引數有:", len(os.Args))
//遍歷os.Args切片,就可以得到所有的命令列輸入引數值
for i, v := range os.Args {
fmt.Printf("args[%v] = %v \n", i ,v)
}
}
//E:\gostudent\src\2020-04-05>go run main.go 999
//命令列的引數有: 2
//args[0] = C:\Users\lxxxxn\AppData\Local\Temp\go-build133979866\b001\exe\main.
//exe
//args[1] = 999
flag包用來決議命令列引數
說明:前面的方式是比較原生的方式,對決議引數不是特別的方便,特別是帶有指定引數形式的命令列
比如:cmd>main.exe -f c:/aaa.txtx -p 200 -u root 這樣形式的命令列,go設計者給提供了flag包,可以方便的決議命令列引數,而且引數順序可以隨意
import (
"flag"
"fmt"
)
func main() {
//定義幾個變數,用于接收命令列的引數值
var user string
var pwd string
var host string
var port int
//&user就是接收用戶命令列中輸入的 -u 后面的引數值
//"u" 就是-u 指定引數
//" " 默認值
//"用戶名,默認為空" 說明
flag.StringVar(&user, "u","","用戶名,默認為空")
flag.StringVar(&pwd,"pwd","","密碼,默認為空")
flag.StringVar(&host,"h","localhost","主機名,默認為localhost")
flag.IntVar(&port,"port",3306,"埠號,默認為3306")
//有一個非常重要的操作轉換,必須呼叫該方法
flag.Parse()
//輸出結果
fmt.Printf("user = %v pwd = %v host = %v port = %v",
user, pwd, host, port)
}
//E:\gostudent\src\2020-04-05>go run main.go -u root -pwd zisefeizhu -h 20.0.0.201 -port 3306
//user = root pwd = zisefeizhu host = 20.0.0.201 port = 3306
Json
Json基本介紹
Json(JavaScript Object Notation)是一種輕量級的資料交換格式,易于人閱讀和撰寫,同時也易于機器決議和生成,key - val
Json是在2001年開始推廣使用的資料格式,目前已經成為主流的資料格式
Json易于機器決議和生成,并有效地提升網路傳輸效率,通常程式在網路傳輸時會先將資料(結構體、map等)序列化成json字串,到接收方得到json字串時,在反序列化恢復成原來的資料型別(結構體、map等),這種方式已然成為各個語言的標準

應用場景

Json資料格式說明
在JS語言中,一切都是物件,因此,任何資料型別都可以通過JSON來表示,例如字串、數字、物件、陣列、map、結構體等
JSON鍵值對是用來保存資料的一種方式
鍵/值對組合中的鍵名寫在前面并用雙引號“”包裹,使用冒號:分隔,然后緊接值:
[{"key1":val1,"key2":val2,"key3":val3,"key4":[val4,val5]},
{"key1":val1,"key2":val2,"key3":val3,"key4":[val4,val5]}]
比如
{"firstName":"Json"}
{"name":"tom","age":18,"address":["北京","上海"]}
[{"name":"zisefeizhu","age":18,"address":["北京","上海"]},
{"name":"jingxing","age":18,"address":["北京","上海"]}]
Jsnon資料在線決議
https://www.json.cn/ 網站可以驗證一個json格式的資料是否正確,尤其是在撰寫比較復雜的json格式資料時,很有用

Json的序列化
json序列化是指,將有key - value 結構的資料型別(比如結構體、map、切片)系列化成json字串的操作
應用案例
演示一下結構體、map和切片的序列化,其它資料型別的序列化類似
import (
"encoding/json"
"fmt"
)
//定義一個結構體
type Monster struct {
Name string
Age int
Bithday string
Sal float64
Skill string
}
func testStruct() {
//演示
monster := Monster{
Name : "牛魔王",
Age : 500,
Bithday : "2001-11-11",
Sal : 8000.0,
Skill : "牛頭拳",
}
//將monster 序列化
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("monster 序列化后 = %v \n", string(data))
}
//演示對切片進行序列化,這個切片[]map[string]interface{}
func testSlice() {
var slice []map[string]interface{}
var m1 map[string]interface{}
//使用map前,需要先make
m1 = make(map[string]interface{})
m1["name"] = "jack"
m1["age"] = "7"
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("monster 序列化后 = %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("monster 序列化后 = %v \n", string(data))
}
func main() {
//演示將結構體、map、切片進行序列化
testStruct()
testMap()
testSlice()
testFloat64()
}
//輸出
// monster 序列化后 = {"Name":"牛魔王","Age":500,"Bithday":"2001-11-11","Sal":8000,"Skill":"牛頭拳"}
//monster 序列化后 = {"address":"洪崖洞","age":30,"name":"紅孩兒"}
//monster 序列化后 = [{"address":"北京","age":"7","name":"jack"},{"address":["墨西哥","夏威夷"],"age":"20","name":"tom"}]
//monster 序列化后 = 2345.67
注意事項
對于結構體的序列化,如果希望序列化后的key的名字可以重新制定,那么可以給struct制定一個tag標簽
import (
"encoding/json"
"fmt"
)
//定義一個結構體
type Monster struct {
Name string `json:"monster_name"` //反射機制 //:兩邊不要分開
Age int `json:"monster_age"`
Bithday string
Sal float64
Skill string
}
func testStruct() {
//演示
monster := Monster{
Name : "牛魔王",
Age : 500,
Bithday : "2001-11-11",
Sal : 8000.0,
Skill : "牛頭拳",
}
//將monster 序列化
data, err := json.Marshal(&monster)
if err != nil {
fmt.Printf("序列號錯誤 err = %v \n", err)
}
//輸出序列化后的結果
fmt.Printf("monster 序列化后 = %v \n", string(data))
}
func main() {
//演示將結構體、map、切片進行序列化
testStruct()
}
//輸出
// monster 序列化后 = {"monster_name":"牛魔王","monster_age":500,"Bithday":"2001-11-11","Sal":8000,"Skill":"牛頭拳"}
Json的反序列化
json反序列化是指,將json字串反序列化成對應的資料型別(比如結構體、map、切片)的操作
應用案例
演示一下將json字串反序列化成結構體、map和切片
import (
"encoding/json"
"fmt"
)
//定義一個結構體
type Monster struct {
Name string
Age int
Birthday string
Sal float64
Skill string
}
//演示將json字串,反序列化成struct
func unmarshalStruct() {
//說明str在專案開發中,是通過網路傳輸獲取到.. 或者是讀取檔案獲取到
str := "{\"Name\":\"牛魔王\",\"Age\":500,\"Birthday\":\"2001-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 monster.Name = %v \n", monster, monster.Name)
}
//演示將json字串,反序列化成map
func unmarshalMap() {
str := "{\"address\":\"洪崖洞\",\"age\":30,\"name\":\"紅孩兒\"}"
//定義一個map
var a map[string]interface{}
//反序列化
//注意:反序列化map,不需要make,因為make操作被封裝到Unmarshal函式
err := json.Unmarshal([]byte(str), &a)
if err != nil {
fmt.Printf("unmarshal err = %v\n", err)
}
fmt.Printf("反序列化后 a = %v\n",a)
}
//演示將json字串,反序列化成切片1
func unmarshalSlice() {
str := "[{\"address\":\"北京\",\"age\":\"7\",\"name\":\"jack\"},"+
"{\"address\":[\"墨西哥\",\"夏威夷\"],\"age\":\"20\",\"name\":\"tom\"}]"
//定義一個slice
var slice []map[string]interface{}
//反序列化,不需要make,因為make操作被封裝到Unmarshal函式
err := json.Unmarshal([]byte(str), &slice)
if err != nil {
fmt.Printf("unmarshal err = %v\n", err)
}
fmt.Printf("反序列化后 slice = %v\n", slice)
}
func main() {
unmarshalStruct()
unmarshalMap()
unmarshalSlice()
}
//輸出
//反序列化后 monster = {牛魔王 500 2001-11-11 8000 牛頭拳} monster.Name = 牛魔王
//反序列化后 a = map[address:洪崖洞 age:30 name:紅孩兒]
//反序列化后 slice = [map[address:北京 age:7 name:jack] map[address:[墨西哥 夏威夷] age:20 name:tom]]
注意事項
? 1) 在反序列化一個json字串時,要確保反序列化后的資料型別和原來序列化前的資料型別一致
? 2) 如果json字串是通程序式獲取到的,則不需要再對“”轉義處理

單元測驗
先看一個需求
在作業中,會遇到這樣的情況,就是去確認一個函式,或者一個模塊的結果是否正確
如:
func addUpper(n int) int {
res := 0
for i := 1; i <= n; i++ {
res += i
}
return res
}
傳統的方法
在main函式中,呼叫addUpper函式,看看實際輸出的結果是否和預期的結果一致,如果一致,則說明函式正確,否則函式有錯誤,然后修改錯誤
//一個被測驗函式
func addUpper(n int) int {
res := 0
for i := 1; i <= n - 1; i++ {
res += i
}
return res
}
func main() {
//傳統的測驗方法,就是在main函式中使用看看結果是否正確
res := addUpper(10)
if res != 55 {
fmt.Printf("addUpper錯誤 回傳值 = %v 期望值 = %v\n ", res, 55)
} else {
fmt.Printf("addUpper正確 回傳值 = %v 期望值 = %v\n", res, 55)
}
}
//addUpper錯誤 回傳值 = 45 期望值 = 55
傳統方法的缺點分析
-
不方便,需要在main函式中去呼叫,這樣就需要去修改main函式,如果現在專案正在運行,就可能去停止專案
-
不利于管理,因為當我們測驗多個函式或者多個模塊時,都需要寫在main函式,不利于我們管理和清晰我們思路
-
引出單元測驗, -> testing測驗框架 可以解決問題
單元測驗
基本介紹
Go語言中自帶有一個輕量級的測驗框架testing和自帶的go test命令來實作單元測驗和性能測驗,testing框架和其它語言中的測驗框架類似,可以基于這個框架寫針對相應函式的測驗用例,也可以基于該框架寫相應的壓力測驗用來,通過單元測驗,可以解決如下問題:
-
確保每個函式是可運行,并且運行結果是正確的
-
確保寫出來的代碼性能是好的
-
單元測驗能及時的發現程式設計或實作的邏輯錯誤,使問題及早暴露,便于問題的定位解決,而性能測驗的重點在于發現程式設計上的一些問題,讓程式能夠在高并發的情況下還能保持穩定
快速入門
使用Go的單元測驗,對addUpper和sub函式進行測驗
特別說明:測驗時,可能需要暫時退出360(因為360可能會認為生成的測驗用例程式是木馬)
演示如何進行單元測驗

單元測驗的運行原理示意圖

單元測驗快速入門總結
-
測驗用例檔案名必須以 _test.go結尾,比如cal_test.go,cal不是固定的
-
測驗用例函式必須以Test開頭,一般來說就是Test+被測驗的函式名,比如TestAddUpper
-
TestAddUpper(t *testing.T)的形參型別必須是 *testing.T
-
一個測驗用例檔案中,可以有多個測驗用例韓式,比如TestAddUpper,TestSub
-
運行測驗用例指令
(1) cmd>go test [如果運行正確,無日志,錯誤時,會輸出日志]
(2) cmd>go test -v [運行正確或是錯誤,都輸出日志]
-
當出現錯誤時,可以使用t.Fatalf來格式化輸出錯誤資訊,并退出程式
-
t.Logf方法可以輸出相應的日志
-
測驗用例函式,并沒有放在main函式中,也可以執行,這就是測驗用例的方便之處
-
PASS表示測驗用例運行成功,FALL表示測驗用例運行失敗
-
測驗單個檔案,一定要帶上被測驗的源檔案
? go test -v cal_test.go cal.go
- 測驗單個方法
? go test -v -test.run TestAddUpper
綜合案例
單元測驗綜合案例要求:
-
撰寫一個Monster結構體,欄位Name、Age、Skill
-
給Monster系結方法Store,可以將一個Monster變數(物件),序列化后保存到檔案中
-
給Monster系結方法ReStore,可以將一個序列化的Monster,從檔案中讀取,并反序列化為Monster物件,檢查反序列化,名字正確
-
編程測驗用例檔案store_test.go,撰寫測驗用例函式TestStore和TestRestore進行測驗
代碼區
monster/monster.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 := "e:/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 := "e:/monster.ser"
data, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Println("ReadFile 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/monster_test.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","紅孩兒",monster.Name)
}
t.Logf("monster.ReStore() 測驗成功!")
}


轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/27351.html
標籤:Go
上一篇:go系列教程-變數
下一篇:Go語言庫系列之email
