(八)文件的操作

1 讀取文件

讀文件用 os 包,需要注意的是在打開一個文件進行操作後需要及時關閉文件。用幾個小例子來說明一下。

1.1 exa1

package main

import (
	"fmt"
	"os"
)

func main()  {

	// 打開一個文件
	// 概念說明:file的叫法
	// 1.file 叫 file對象
	// 2.file 叫 file指針
	// 3.file 叫 file文件句柄
	file, err := os.Open("d:/test.txt")
	if err != nil {
		fmt.Println("Open file err=", err)
	}

	// 輸出以下內容可以看出file就是一個指針
	fmt.Printf("file=%v", file)  // 輸出file=&{0xc000084780}

	// 關閉文件
	err = file.Close()
	if err != nil {
		fmt.Println("close file err=", err)
	}

}

1.2 exa2

讀文件的內容並顯示在終端(帶緩衝區的方式)使用os.Open(), file.close(), bufio.NewReader(), reader.ReadString函數和方法。

package main

import (
	"fmt"
	"os"
	"bufio"
	"io"
)

func main()  {

	// 打開文件
	file, err := os.Open("d:/test.txt")

	if err != nil {
		fmt.Println("Open file err=", err)
	}

	// 當函數退出時候,要及時關閉文件句柄,否則會有內存泄漏
	defer file.Close()

	// 創建一個帶緩衝的 *Reader
	// 默認緩衝區 4096
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n')  // 每讀到一個換行就結束一次
		if err == io.EOF {  // io.EOF代表文件的末尾
			break
		}
		// 輸出內容
		fmt.Print(str)
	}

	fmt.Println("文件讀取結束")
}
// 需要注意的是如果使用 if err == io.EOF ,那麼在我們要讀取的文件中最後一行一定是一個換行
// 否則不能讀取到最後一行

1.3 exa3

讀取文件的內容並顯示在終端(使用ioutil一次將整個文件讀入到內存中)這種方式適用於文件不大的情況,相關方法和函數(ioutil.ReadFile),此函數封裝了打開和關閉的操作

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {

	// 使用 ioutil.ReadFile 一次性將文件讀取到位
	file := "d:/test.txt"
	content, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Printf("read file err = %v", err)
	}

	// 把讀取到的內容顯示到終端
	fmt.Printf("%v", content)  // []byte 會以byte切片的樣式輸出
	// 因爲沒有顯式Open,因此也不需要顯式的Close文件文件的Open和Close功能都被封裝到函數中

	fmt.Printf("%s", content)  // 可以完整將字符串輸出
	//fmt.Printf("%v", string(content))
}

1.4 判斷文件是否存在

golang中判斷文件或文件夾是否存在的方法是使用 os.Stat() 函數返回的錯誤值進行判斷。

package main

import (
	"fmt"
	"os"
)

// 判斷文件是否存在
func PathExists(path string) (bool, error) {

	_, err := os.Stat(path)
	if err == nil {  // 如果err爲空,則證明文件存在
		return true, nil
	}


	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err

}

func main() {

	filePath := "d:/test.txt"

	b, err := PathExists(filePath)
	if b == true && err == nil {
		fmt.Printf("%v文件存在", filePath)
		return
	}
	fmt.Printf("%v文件不存在", filePath)

}

2 寫入文件內容

需要用到 os.OpenFile 函數
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
說明: os.OpenFile 它會使用指定的選項(如os.o_RDONLY等)、指定的模式(如0666等,此選項只在linux系統中生效),如果成功,則返回的對象可以用於 I/O 操作

2.1 exa1

創建一個新文件,寫入內容 5 句 “hello, Gardon”

package main

import (
	"fmt"
	"bufio"
	"os"
)

func main() {

	// 創建一個新文件,寫入內容 5 句 "hello, Gardon"
	// 1.打開文件 d:/abc.txt
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_CREATE | os.O_WRONLY, 0666)
	if err != nil {
		fmt.Printf("open file err = %v", err)
		return
	}

	// 準備寫入5句話
	str := "hello, Gardon\r\n" // 表示換行
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}

	// 及時關閉file句柄
	defer file.Close()

	// 因爲writer是帶緩存,因此在調用WriteString方法時
	// 其實內容是先寫入到緩存的,所以需要調用Flush方法,將緩衝的數據真正寫入到文件中,
	// 否則文件中會沒有數據
	writer.Flush()
}

2.2 exa2

打開一個已經存在的文件,將原來的內容覆蓋成新的內容10句 “你好,我是老大”

package main

import (
	"fmt"
	"bufio"
	"os"
)

func main() {

	// 打開一個已經存在的文件,將原來的內容覆蓋成新的內容10句 "你好,我是老大"
	// 1.打開文件 d:/abc.txt
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_TRUNC , 0666)
	if err != nil {
		fmt.Printf("open file err = %v", err)
		return
	}

	// 及時關閉file句柄
	defer file.Close()

	// 寫入10句 "你好,我是老大"
	str := "你好,我是老大\r\n" // 表示換行
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString(str)
	}

	writer.Flush()
}

2.3 exa3

打開一個存在的文件,在原來的內容追加內容 'ABC!ENGHSK’

package main

import (
	"fmt"
	"bufio"
	"os"
)

func main() {

	// (3)打開一個存在的文件,在原來的內容追加內容 'ABC!ENGHSK'
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_APPEND , 0666)
	if err != nil {
		fmt.Printf("open file err = %v", err)
		return
	}

	// 及時關閉file句柄
	defer file.Close()

	// 寫入10句 'ABC!ENGHSK'
	str := "'ABC!ENGHSK'\r\n" // 表示換行
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString(str)
	}

	writer.Flush()
}

2.4 exa4

打開一個存在的文件,將原來的內容讀出顯示在終端,並且追加5句"hello, 北京"

package main

import (
	"fmt"
	"bufio"
	"os"
	"io"
)

func main() {

	// (4)打開一個存在的文件,將原來的內容讀出顯示在終端,並且追加5句"hello, 北京"
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_RDONLY | os.O_APPEND , 0666)
	if err != nil {
		fmt.Printf("open file err = %v", err)
		return
	}

	// 及時關閉file句柄
	defer file.Close()

	// 先讀取原來的內容
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF {
			break
		}
		// 顯示到終端
		fmt.Print(str)
	}

	// 寫入10句 "hello, 北京"
	str := "hello, 北京\r\n" // 表示換行
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}

	writer.Flush()
}

2.5 exa5

編寫一個程序,將一個文件的內容寫入到另一個文件。這兩個文件已經存在

package main

// 編寫一個程序,將一個文件的內容寫入到另一個文件。這兩個文件已經存在
// 使用ioutil.ReadFile / ioutil.WriteFile

import (
	"fmt"
	"io/ioutil"
)

func main() {

	// 將d:/abc.txt文件內容導入到 f:/kkk.txt
	// 聲明兩個文件名
	file1Path := "d:/abc.txt"
	file2Path := "f:/kkk.txt"

	// 1.將d:/abc.txt內容讀取到內存
	content, err := ioutil.ReadFile(file1Path)
	if err != nil {
		// 說明文件讀取有錯誤
		fmt.Printf("read file err = %v\n", err)
		return
	}

	// 2.將讀取到的內容寫入到 f:/kkk.txt
	err = ioutil.WriteFile(file2Path, content, 0666)
	if err != nil {
		fmt.Printf("write file error = %v\n", err)
	}

}

3 文件的拷貝

3.1 拷貝非文本文件的文件

package main

import (
	"fmt"
	"os"
	"io"
	_ "io/ioutil"
	"bufio"
)

// 將一張圖片/電影/mp3文件拷貝到另一個文件中 需要用到io包(注意不是文本文件)
// io.Copy

// 編寫一個函數,接收兩個文件路徑 srcFileName dstFileName
func CopyFile(dstFileName string, srcFileName string) (written int64, err error) {
	srcfile, err := os.Open(srcFileName)
	if err != nil {
		fmt.Printf("Open file err = %v", err)
	}
	defer srcfile.Close()

	//通過srcfile句柄獲取到 Reader
	reader := bufio.NewReader(srcfile)

	// 打開dstFileName(由於這個文件可能不存在,所以不能用os.Open來打開)
	dstFile, err := os.OpenFile(dstFileName, os.O_CREATE | os.O_WRONLY, 0666)
	if err != nil {
		fmt.Printf("open file err = %v", err)
		return
	}

	// 通過dstFile,獲取到writer
	writer := bufio.NewWriter(dstFile)
	defer dstFile.Close()

	return io.Copy(writer, reader)
}

func main() {

	// 將 d:/flower.jpg 拷貝到 f:/abc.jpg

	// 調用CopyFile完成文件的拷貝
	srcFile := "d:/flower.jpg"
	dstFile := "f:/abc.jpg"

	_, err := CopyFile(dstFile, srcFile)
	if err == nil {
		fmt.Println("Copy Successd!")
	} else {
		fmt.Println("Copy error info = %v", err)
	}
}

3.2 拷貝文本文件

package main

import (
	"fmt"
	"os"
	"io"
	_ "io/ioutil"
	"bufio"
)

// 將文本文件拷貝到另一個文件中 需要用到io包,
// io.Copy

// 編寫一個函數,接收兩個文件路徑 srcFileName dstFileName
func CopyFile(dstFileName string, srcFileName string) (written int64, err error) {
	srcfile, err := os.Open(srcFileName)
	if err != nil {
		fmt.Printf("Open file err = %v", err)
	}
	defer srcfile.Close()

	//通過srcfile句柄獲取到 Reader
	reader := bufio.NewReader(srcfile)


	// 打開dstFileName(由於這個文件可能不存在,所以不能用os.Open來打開)
	dstFile, err := os.OpenFile(dstFileName, os.O_CREATE | os.O_WRONLY, 0666)
	if err != nil {
		fmt.Printf("open file err = %v", err)
		return
	}

	// 通過dstFile,獲取到writer
	writer := bufio.NewWriter(dstFile)

	// 循環把src讀取的每行數據寫入到writer
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF {  // io.EOF代表文件的末尾
			break
		}
		// 顯示到終端
		writer.WriteString(str)
	}

	// flush把緩衝的數據寫入到真實文件中
	writer.Flush()
	defer dstFile.Close()

	return io.Copy(writer, reader)
}

func main() {

	// 將 d:/flower.txt 拷貝到 f:/abc.txt

	srcFile := "d:/flower.txt"
	dstFile := "f:/abc.txt"

	_, err := CopyFile(dstFile, srcFile)
	if err == nil {
		fmt.Println("Copy Successd!")
	} else {
		fmt.Println("Copy error info = %v", err)
	}
}

4 命令行參數

如果希望獲取到命令行輸入的各種參數,可以使用os.Args,它是一個string切片,用來存儲所有的命令行參數

4.1 exa1

package main

import (
	"fmt"
	"os"
)

func main() {

	fmt.Println("命令行的參數有", len(os.Args))
	for i, v := range os.Args {
		fmt.Printf("args[%v]=%v\n", i, v)
	}

	/*
	D:\goproject\src\go_code\file\commandLineArgs>main.exe D:\goproject\src\go_code 99
	命令行的參數有 3
	args[0]=main.exe
	args[1]=D:\goproject\src\go_code
	args[2]=99
	*/
}

4.2 exa2 (flag)

golang中可以用flag包來解析命令行參數,exa1中使用的 os.Args 來獲取命令行參數比較原生,解析不是特別方便,尤其是帶有指定參數形式的命令行

package main

import (
	"fmt"
	"flag"
)

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)
	/*
	可以不用指定順序
	D:\goproject\src\go_code\file\commandLineArgs\flag>go build -o flag.exe main.go
	D:\goproject\src\go_code\file\commandLineArgs\flag>flag.exe -u root -pwd 12335465 -h 192.168.0.1 -port 8080
	user=root pwd=12335465 host=192.168.0.1 port=8080

	默認值
	D:\goproject\src\go_code\file\commandLineArgs\flag>flag.exe -u root -pwd 12335465
	user=root pwd=12335465 host=localhost port=3306

	*/
}

5 json序列化及反序列化

json是一種輕量級的數據交換格式,易於人爲閱讀和編寫,同時也易於機器的解析和生成,是一種key-value的格式。
由於json語言中,一切都是對象。因此,任何的數據類型都可以通過json來表示,例如,字符串、數字、對象、數組、map、結構體等等。

json解析的網站

5.1 json的序列化

json的序列化是指,將現有的key-value結構的數據類型(比如結構體、map、切片)序列化成json字符串的操作。

package main

import (
	"fmt"
	"encoding/json"
)

// 定義一個結構體
type Monster struct {
	// 如果希望json過後字段的名稱,需要打一個tag
	Name string `json:"name"`  // 通過反射機制實現的
	Age int `json:"age"`
	Birthday string
	Sal float64
	Skill string
}

func testStruct()  {
	// 聲明結構體實例
	monster := Monster{
		Name: "牛魔王",
		Age: 500,
		Birthday: "2011-11-11",
		Sal: 10000.0,
		Skill: "牛頭拳",
	}

	// 將monster序列化
	data, err := json.Marshal(&monster)
	if err != nil {
		fmt.Printf("序列化錯誤 = %v\n", err)
	}
	// 輸出序列化後的結果
	fmt.Printf("monster序列化後 = %v\n", string(data))  // 由於data是bate切片,所以需要轉換成字符串

}

func testMap()  {
	// 定義一個map
	var a map[string]interface{}

	// 使用map,需要make
	a = make(map[string]interface{})
	a["name"] = "紅孩兒"
	a["age"] = 200
	a["address"] = "洪崖洞"

	// 將map序列化
	data, err := json.Marshal(a)  // 由於a map本身就是一個指針,所以不需要加引用地址的符號 &
	if err != nil {
		fmt.Printf("序列化錯誤 = %v\n", err)
	}
	// 輸出序列化後的結果
	fmt.Printf("a map序列化後 = %v\n", string(data))
}

// 對切片進行序列化
func testSlice() {
	var slice []map[string]interface{}
	var m1 map[string]interface{}
	m1 = make(map[string]interface{})
	m1["name"] = "jack"
	m1["age"] = 7
	m1["address"] = "天津"
	slice = append(slice, m1)

	var m2 map[string]interface{}
	m2 = make(map[string]interface{})
	m2["name"] = "merry"
	m2["age"] = 30
	m2["address"] = [2]string{"北京", "墨西哥"}
	slice = append(slice, m2)

	// 將切片序列化
	data, err := json.Marshal(slice)  // 由於a map本身就是一個指針,所以不需要加引用地址的符號 &
	if err != nil {
		fmt.Printf("序列化錯誤 = %v\n", err)
	}
	// 輸出序列化後的結果
	fmt.Printf("slice序列化後 = %v\n", string(data))
}

// 對基本數據類型序列化
func testFloat64()  {
	var num1 float64 = 1233.45

	data, err := json.Marshal(num1)  // 由於a map本身就是一個指針,所以不需要加引用地址的符號 &
	if err != nil {
		fmt.Printf("序列化錯誤 = %v\n", err)
	}
	// 輸出序列化後的結果
	fmt.Printf("num1序列化後 = %v\n", string(data))

}

func main() {

	// (1)將結構體進行序列化
	testStruct() // monster序列化後 = {"name":"牛魔王","age":500,"Birthday":"2011-11-11","Sal":10000,"Skill":"牛頭拳"}

	// (2)將map進行序列化
	testMap()  // a map序列化後 = {"address":"洪崖洞","age":200,"name":"紅孩兒"}

	// (3)將切片進行序列化
	testSlice()  // slice序列化後 = [{"address":"天津","age":7,"name":"jack"},{"address":["北京","墨西哥"],"age":30,"name":"merry"}]

	// (4)對基本數據類型序列化
	testFloat64()  // num1序列化後 = 1233.45

	// 可以去 json.cn 網站上對json序列進行解析,看是否正確
}

5.2 json的反序列化

json的反序列化是指將json字符串反序列化成對應的數據類型(比如結構體、map、切片)的操作。

package main

// 反序列化

import (
	"fmt"
	"encoding/json"
)

// 定義一個結構體
type Monster struct {
	Name string
	Age int
	Birthday string
	Sal float64
	Skill string
}

// 將json字符串,反序列化成struct
func unmarshalStruct() {
	// str在實際開發項目中是通過網絡傳輸獲取到,或者是讀取文件獲取到
	str := "{\"name\":\"牛魔王\",\"age\":500,\"Birthday\":\"2011-11-11\",\"Sal\":10000,\"Skill\":\"牛頭拳\"}"

	// 定義一個Monster實例
	var monster Monster

	err := json.Unmarshal([]byte(str), &monster)  // 這裏需要注意,反序列化時候需要將str轉成byte數組
	if err != nil {
		fmt.Printf("unmarshal err = %v\n", err)
	}
	fmt.Printf("反序列化後 monster = %v monster.Name=%v\n", monster, monster.Name)

}

// 反序列化map
func unmarshalMap() {
	str := "{\"address\":\"洪崖洞\",\"age\":200,\"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)
}

// 將json字符串,反序列化成切片
func unmarshalSlice() {

	str := "[{\"address\":\"天津\",\"age\":7,\"name\":\"jack\"}," +
		"{\"address\":[\"北京\",\"墨西哥\"],\"age\":30,\"name\":\"merry\"}]"

	// 定義一個Slice
	var slice []map[string]interface{}
	// 不需要make
	err := json.Unmarshal([]byte(str), &slice)
	if err != nil {
		fmt.Printf("unmarshal err = %v\n", err)
	}
	fmt.Printf("反序列化後 a = %v\n", slice)

}

func main()  {

	// (1)反序列化mstruct結構體
	unmarshalStruct()  // 反序列化後 monster = {牛魔王 500 2011-11-11 10000 牛頭拳}  monster.Name=牛魔王

	// (2)反序列化map
	unmarshalMap()  // 反序列化後 a = map[address:洪崖洞 age:200 name:紅孩兒]

	// (3)反序列化Slice
	unmarshalSlice()

	// 注意:
	// 1、反序列化一個json字符串時,要確保反序列化後的數據類型和原來序列化前的數據類型一致。
	// 2、如果json字符串是通過程序獲取的,那麼就不需要對雙引號進行 \ 轉移處理
}

6 單元測試

正常情況下在寫好代碼後需要對函數的功能進行一個測試,

6.1 傳統函數功能測試

傳統的函數功能測試就是單一的對函數的返回值或者功能進行判斷,看是否符合我們的預期要求,比如我們寫一個計算數值和的函數,在測試的時候我們就對結果進行判斷,看結果是否是我們預期的值,進而實現函數的測試。

6.2 testing測試框架

Go語言中自帶有一個輕量級的測試框架testing和自帶的 go test 命令來實現單元測試和性能測試。

6.2.1 exa1

cal.go

package main

func addUpper(n int) int {

	res := 0
	for i := 0; i <= n; i++ {
		res += i
	}
	return res

}

cal_test.go

package main

import (
	"fmt"
	"testing"  // 引用go的testing框架包
)

// 編寫一個測試用例,去測試剛纔寫的 addUpper 是否正確
func TestAddUpper(t *testing.T)  {

	// 調用
	res := addUpper(10)
	if res != 55 {
		// fmt.Printf("AddUpper(10) 執行錯誤,期望值是=%v 實際值=%v\n", 55, res)
		t.Fatalf("AddUpper(10) 執行錯誤,期望值是=%v 實際值=%v\n", 55, res)
	}

	// 如果正確,輸出日誌
	t.Logf("AddUpper(10) 執行正確...")

}

func TestHello(t *testing.T) {
	fmt.Println("TestHello()被調用...")
}

測試

/* 輸出信息
   D:\goproject\src\go_code\testingDemo\testCase>go test -v
   === RUN   TestAddUpper
   --- PASS: TestAddUpper (0.00s)
	   cal_test.go:19: AddUpper(10) 執行正確...
   PASS
   ok      go_code/testingDemo/testCase    0.237s

 */

6.2.2 exa2

編寫一個結構體Monster,定義幾個字段,給Monster綁定Store序列化方法,序列化後保存到一個文件中,再給Monster綁定ReStore反序列化方法,可以將一個序列化的Monster從文件中讀取內容,並反序列化爲Monster對象,檢查內容是否正確,編寫測試文件對結果進行測試。
monster.go

package main

import (
	"fmt"
	"io/ioutil"
	"encoding/json"
)

type Monster struct {
	Name string
	Age int
	Skill string

}

func (m *Monster) Store() bool {

	data, err := json.Marshal(&m)
	if err != nil {
		fmt.Printf("序列化錯誤 = %v\n", err)
	}

	// 保存到文件
	filePath := "d:/monster.ser"
	err = ioutil.WriteFile(filePath, data, 066)
	if err != nil {
		fmt.Println("Writer file err = ", err)
	}

	return true

}

func (m *Monster) ReStore() bool {

	// 先從文件中讀取序列化的字符串
	filePath := "d:/monster.ser"
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		fmt.Println("Read file err = ", err)
		return false
	}

	err = json.Unmarshal(data, &m) 
	if err != nil {
		fmt.Printf("unmarshal err = %v\n", err)
		return false
	}
	return true

}

monster_test.go

package main


import (
	_ "fmt"
	"testing"
)

func TestMonster_Store(t *testing.T) {
	monster := Monster{
		Name: "紅孩兒",
		Age: 20,
		Skill: "吐火",
	}

	res := monster.Store()
	if !res {
		t.Fatalf("monster.Store() test err,希望是 = %v 實際是 = %v", true, res)
	}
	t.Logf("monster.Store() test successed.")
}

func TestMonster_ReStore(t *testing.T) {

	// 先創建一個monster實例
	var monster Monster

	// 通過反序列化
	res := monster.ReStore()
	if !res {
		t.Fatalf("monster.Store() test err,希望是 = %v 實際是 = %v", true, res)
	}

	if monster.Name != "紅孩兒" {
		t.Fatalf("monster.Store() test err,希望是 = %v 實際是 = %v", "紅孩兒", res)
	}
	t.Logf("monster.ReStore() test successed.")

}

6.3 注意事項

  • (1)測試用例文件必須以 *_test.go 結尾。比如cal_test.go, cal是不固定的,可變的。
  • (2)測試用例函數必須以Test開頭,一般來說就是 Test+被測試的函數名,比如 TestAddUpper。
  • (3)TestAddUpper(t *testing.T)的形參類型必須是 *testing.T
  • (4)一個測試用例文件中,可以有多個測試用力函數,比如TestAddUpper、TestSub
  • (5)運行測試用例指令
	1) cmd> go test [如果運行正確,無日誌;錯誤時,會輸出日誌]
    2) cmd> go test -v [運行正確或是錯誤,都輸出日誌]
  • (6)當出現錯誤時,可以使用 t.Fatalf 來格式化輸出錯誤信息,並退出程序
  • (7)t.Logf方法可以輸出相應的日誌
  • (8)測試用例函數,並沒有放在main函數中,也是執行了,這就是測試用例的方便之處
  • (9)PASS表示測試用例運行成功,FAIL表示測試用例運行失敗
  • (10)測試單個文件,一定要帶上被測試的源文件
    go test -v cal_test.go cal.go
    go test -v cal.go cal_test.go   // 測試文件和被測試文件的順序可以交換
  • (11)測試單個方法
    go test -v -test.run TestAddUpper
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章