<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] ## <span style="font-size:15px">**一、testing包說明**</span> * 測試用例文件不會參與正常源碼的編譯,不會被包含到可執行文件中; * 測試用例的文件名必須以`_test.go`結尾; * 需要使用 import 導入 testing 包; * 測試函數的名稱要以`Test`或`Benchmark`開頭,后面可以跟任意字母組成的字符串,但第一個字母必須大寫,例如 TestAbc(),一個測試用例文件中可以包含多個測試函數; * 單元測試則以`(t *testing.T)`作為參數,性能測試以`(t *testing.B)`做為參數; * 測試用例文件使用`go test`命令來執行,源碼中不需要 main() 函數作為入口,所有以`_test.go`結尾的源碼文件內以`Test`開頭的函數都會自動執行。 ## <span style="font-size:15px">**二、功能測試(t \*testing.T)**</span> | 庫函數 | 說明 | | --- | --- | | Log(args ...any) | 打印日志 | | Logf(format string, args ...any) | 格式化其參數打印日志 | | Fatal(args ...any) | 打印日志,執行FailNow()函數 | | Fatalf(format string, args ...any) | 打印日志,執行FailNow()函數 | | Error(args ...any) | 打印錯誤日志后,執行Fail()函數 | | Errorf(format string, args ...any) | 打印錯誤日志后,執行Fail()函數 | | Run(name string, f func(t *T)) bool | 運行單測函數 | | Cleanup(f func()) | 作用類似于在函數中執行defer來做程序執行后的后續操作,比如釋放資源、清除依賴等。<br>參考:https://www.cnblogs.com/YYRise/p/12376689.html#%E4%BD%BF%E7%94%A8-cleanup | | Deadline() (deadline time.Time, ok bool) | 設置測試的超時時間 | | Fail() | Fail將函數標記為已失敗,但仍在繼續執行。 | | Failed() | 檢查函數是否已失敗 | | FailNow() | 將函數標記失敗并立即停止執行 | | Helper() | Helper 函數用于將調用函數標記為測試輔助的函數。有時候測試中可能會有一些重復的代碼或者需要共享的輔助函數,這時就可以使用 Helper 函數來避免重復代碼,提高測試代碼的可維護性。Helper 函數可以被測試函數調用,但是它們不會被測試框架視為獨立的測試用例。這樣可以使測試函數更加清晰和簡潔 | | Name() | 返回正在運行的測試或基準測試的名稱 | | Parallel() | Parallel 函數用于標記測試函數可以并行執行。當測試函數被標記為 Parallel 時,測試框架會在執行測試時創建多個 goroutine 并行執行這些測試函數,從而加快測試的執行速度 | | Setenv(key, value string) | 設置環境變量 | | Skip(args ...any) | 打印日志,并執行SkipNow() | | Skipf(format string, args ...any) | 打印日志,并執行SkipNow() | | SkipNow() | 跳過當前測試,而不會導致測試失敗 | | Skipped() bool | 檢查函數是否跳過 | | TempDir() string | 用于創建一個臨時目錄,輔助測試 | **Cleanup與 defer 的區別:** 1. 執行時機不同:Cleanup 函數會在測試函數執行完畢后立即執行,而 defer 語句會在當前函數返回前執行。 2. 調用方式不同:Cleanup 函數需要在測試函數中顯式調用,而 defer 語句可以在任何函數中使用。 3. 多個調用的處理方式不同:Cleanup 函數可以多次調用,每次調用都會按照調用順序執行清理操作;而 defer 語句只能在當前函數中使用一次,多次調用會按照后進先出的順序執行。 4. 錯誤處理方式不同:Cleanup 函數的清理操作不會影響測試函數的錯誤處理,即使清理操作中發生錯誤,測試函數的錯誤仍然會被捕獲和報告;而 defer 語句中的清理操作如果發生錯誤,可能會影響當前函數的錯誤處理。 5. 總的來說,Cleanup 函數適用于在測試函數執行完畢后進行一些清理操作,而 defer 語句適用于在函數返回前執行一些必要的清理操作。 ``` func TestMD5(t *testing.T) { if MD5("xxxxx") != "fb0e22c79ac75679e9881e6ba183b354" { t.Fatalf("執行失敗") } t.Log("執行成功") } ``` ## <span style="font-size:15px">**三、性能測試(b *testing.B)**</span> * 用例需以 Benchmark 為前綴 * 參數必須是 b *testing.B * 函數返回值必須為空 | 庫函數 | 說明 | | --- | --- | | Log(args ...any) | 打印日志 | | Logf(format string, args ...any) | 格式化其參數打印日志 | | Fatal(args ...any) | 打印日志,執行FailNow()函數 | | Fatalf(format string, args ...any) | 打印日志,執行FailNow()函數 | | Error(args ...any) | 打印錯誤日志后,執行Fail()函數 | | Errorf(format string, args ...any) | 打印錯誤日志后,執行Fail()函數 | | Run(name string, f func(t *B)) bool | 運行單測函數 | | Cleanup(f func()) | 作用類似于在函數中執行defer來做程序執行后的后續操作,比如釋放資源、清除依賴等。<br>參考:https://www.cnblogs.com/YYRise/p/12376689.html#%E4%BD%BF%E7%94%A8-cleanup | | Fail() | Fail將函數標記為已失敗,但仍在繼續執行。 | | Failed() | 檢查函數是否已失敗 | | FailNow() | 將函數標記失敗并立即停止執行 | | Helper() | Helper 函數用于將調用函數標記為測試輔助的函數。有時候測試中可能會有一些重復的代碼或者需要共享的輔助函數,這時就可以使用 Helper 函數來避免重復代碼,提高測試代碼的可維護性。Helper 函數可以被測試函數調用,但是它們不會被測試框架視為獨立的測試用例。這樣可以使測試函數更加清晰和簡潔 | | Name() | 返回正在運行的測試或基準測試的名稱 | | Setenv(key, value string) | 設置環境變量 | | Skip(args ...any) | 打印日志,并執行SkipNow() | | Skipf(format string, args ...any) | 打印日志,并執行SkipNow() | | SkipNow() | 跳過當前測試,而不會導致測試失敗 | | Skipped() bool | 檢查函數是否跳過 | | TempDir() string | 用于創建一個臨時目錄,輔助測試 | | N | 表示循環的次數,因為需要反復調用測試代碼來評估性能。b.N 的值會以1, 2, 5, 10, 20, 50, …這樣的規律遞增下去直到運行時間大于1秒鐘,由于程序判斷運行時間穩定才會停止運行 | | ReportAllocs() | ReportAllocs為該基準啟用malloc統計信息。它相當于設置test.benchmem,但它只影響調用ReportAllocs的基準函數。 | | ReportMetric(n float64, unit string) | 用于報告性能測試中收集到的指標數據,其中 n 是浮點數類型的指標值,unit 是指標的單位。 | | ResetTimer() | ResetTimer 是 Golang 中 testing 包中的一個函數,用于重置計時器,通常用于基準測試中。在基準測試中,我們通常會使用 StartTimer 來開始計時,然后執行被測試的代碼,最后使用 StopTimer 來停止計時。在循環測試中,ResetTimer 可以用于重置計時器,以便在同一段代碼上多次運行計時,而不必重新創建計時器。 | | RunParallel(body func(*PB)) | 用于在多個goroutine中并行運行測試函數。在調用 RunParallel 方法時,需要提供一個函數,該函數會在多個goroutine中并行執行。 | | SetBytes(n int64) | SetBytes 方法用于設置每次操作處理的數據大小。這個方法通常用于基準測試中,可以幫助測試者評估在不同數據負載下的性能表現。通過設置數據大小,可以更好地模擬實際應用中的場景,從而更準確地評估代碼的性能。 | | SetParallelism(p int) | 用于設置并行測試的數量 | | StartTimer() | 開始計時測試。此函數在測試啟動前自動調用,但也可以用于在調用StopTimer后恢復計時 | | StopTimer() |停止計時| **運行命令:** 性能測試命令為 go test [參數],比如 go test -bench=. ,具體的命令參數及含義如下: * -bench regexp 性能測試,運行指定的測試函。 * -bench . 運行所有的benchmark函數測試,指定名稱則只執行具體測試方法而不是全部。 * -benchmem 性能測試的時候顯示測試函數的內存分配的統計信息。 * -count n 運行測試和性能多少此,默認一次。 * -run regexp 只運行特定的測試函數。 * -timeout t 測試時間如果超過 t 則panic,默認10分鐘。 * -v 顯示測試的詳細信息。 ``` func BenchmarkValid(b *testing.B) { str := `{"foo":"bar"}` b.ResetTimer() for i := 0; i < b.N; i++ { json.Valid(([]byte(str))) } } [root@master-4vfjd util]# go test -bench=BenchmarkValid goos: linux goarch: amd64 pkg: sangfor.com/xdr/xlink/common/util cpu: Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz BenchmarkValid-4 9462286 133.8 ns/op PASS ok sangfor.com/xdr/xlink/common/util 3.931s ``` **運行結果含義:** * BenchmarkValid 是性能測試函數名稱 * -4 表示 GOMAXPROCS 的值為4 * 9462286 表示一共執行了9462286次,即b.N的值 * 133.8 ns/op 表示平均每次操作花費了 133.8 納秒 ## <span style="font-size:15px">**四、代碼覆蓋率測試**</span> * 執行并輸出覆蓋率:`go?test?-v?-cover` * 將 cover 的詳細信息保存到cover.out 中:`go?test?-cover?-coverprofile=cover.out?-covermode=count` * -cover?允許代碼分析 * -covermode?代碼分析模式(set:是否執行;count:執行次數;atomic:次數,并發執行) * -coverprofile?輸出結果文件 * go?tool?cover?-func=cover.out ### <span style="font-size:15px">1. 測試單元測試的覆蓋率</span> ``` // 結果顯示只有43.9%覆蓋率 [root@master-4vfjd util]# go test -v -cover === RUN Test2 httpChainUtil_test.go:113: http://127.0.0.1:2563 --- PASS: Test2 (0.00s) === RUN Test1 httpChainUtil_test.go:140: http://127.0.0.1:12707 --- PASS: Test1 (0.12s) === RUN TestBytes2ReadOnlyString === RUN TestBytes2ReadOnlyString/Test_Bytes2ReadOnlyString --- PASS: TestBytes2ReadOnlyString (0.00s) PASS coverage: 43.9% of statements ok sangfor.com/xdr/xlink/common/util 0.367s ``` ### <span style="font-size:15px">2. 查看每個函數的覆蓋率</span> ``` [root@master-4vfjd util]# go test -cover -coverprofile=cover.out -covermode=count ... [root@master-4vfjd util]# go tool cover -func=cover.out ... sangfor.com/xdr/xlink/common/util/rsaCrypt.go:19: ReadFile 76.9% sangfor.com/xdr/xlink/common/util/rsaCrypt.go:42: RsaDecrypt 0.0% sangfor.com/xdr/xlink/common/util/rsaCrypt.go:81: RsaEncrypt 64.0% total: (statements) 43.9% ``` ### <span style="font-size:15px">3. 使用html 的方式查看具體的覆蓋情況</span> ``` [root@master-4vfjd util]# go tool cover -html=cover.out HTML output written to /tmp/cover629243415/coverage.html ``` ![](https://img.kancloud.cn/02/73/0273f43067f78f8330f33ae45b731bdf_1137x412.png) ## <span style="font-size:15px">**五、并發測試**</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;默認情況下,指定包的測試是按照順序執行的,但也可以通過在測試的函數內部使用`t.Parallel()`來標志某些測試也可以被安全的并發執行。在并行執行的情況下,只有當那些被標記為并行的測試才會被并行執行,所以只有一個測試函數時是沒意義的。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在并發情況下,同時運行的測試的數量默認取決于`GOMAXPROCS`。它可以通過`-parallel n`被指定(`go test -parallel 4`) **并發測試實例:** ``` package main import ( "fmt" "testing" "time" ) func TestParallel(t *testing.T) { // 設置測試的并行數量 t.Parallel() // 模擬一個需要耗時的測試 time.Sleep(1 * time.Second) // 打印測試結果 fmt.Println("Test completed") } func TestSequential(t *testing.T) { // 模擬一個需要耗時的測試 time.Sleep(1 * time.Second) // 打印測試結果 fmt.Println("Test completed") } func main() { // 運行測試 testing.Main(func(pat, str string) (bool, error) { return true, nil }, []testing.InternalTest{ {Name: "TestParallel", F: TestParallel}, {Name: "TestSequential", F: TestSequential}, }, nil, nil) } ``` ## <span style="font-size:15px">**六. 競爭檢查**</span> 數據競爭是指兩個或多個goroutine并發訪問同一塊內存,并且至少其中一個是寫操作,可能會導致panic等問題,如`panic: runtime error: invalid memory address or nil pointer dereference` `go run-v -race`:-race 參數是指開啟競爭檢查,可以檢測并發操作是否安全。 `go test -race`:執行測試并開啟競爭檢查 **實例:** ``` // vim race.go package main import "fmt" func main() { done := make(chan bool) m := make(map[string]string) m["name"] = "world" go func() { m["name"] = "data race" done <- true }() fmt.Println("Hello,", m["name"]) <-done } ``` ``` [root@master-4vfjd util]# go run -race race.go Hello, world ================== WARNING: DATA RACE Write at 0x00c000070150 by goroutine 7: runtime.mapassign_faststr() /root/go1.18.10/go/src/runtime/map_faststr.go:203 +0x0 main.main.func1() /root/XLink/common/util/race.go:10 +0x50 Previous read at 0x00c000070150 by main goroutine: runtime.mapaccess1_faststr() /root/go1.18.10/go/src/runtime/map_faststr.go:13 +0x0 main.main() /root/XLink/common/util/race.go:13 +0x16b Goroutine 7 (running) created at: main.main() /root/XLink/common/util/race.go:9 +0x14e ================== ================== WARNING: DATA RACE Write at 0x00c000100088 by goroutine 7: main.main.func1() /root/XLink/common/util/race.go:10 +0x5c Previous read at 0x00c000100088 by main goroutine: main.main() /root/XLink/common/util/race.go:13 +0x175 Goroutine 7 (running) created at: main.main() /root/XLink/common/util/race.go:9 +0x14e ================== ``` &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在這段代碼中,goroutine 7 和主goroutine 同時訪問了map m,其中一個是寫操作,另一個是讀操作,導致了數據競爭。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;具體來說,goroutine 7 在匿名函數中對map m進行了寫操作,而主goroutine 中的 fmt.Println("Hello,", m["name"]) 則是對map m進行了讀操作,這就引發了數據競爭。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在運行時,Go語言的競爭檢測器(race detector)檢測到了這個問題,并給出了警告信息,指出了具體的數據競爭情況,包括讀寫的內存地址和涉及的goroutine。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;要解決這個問題,可以使用互斥鎖(mutex)來保護并發訪問的map,確保同一時間只有一個goroutine在訪問該map,從而避免數據競爭的發生。 ``` // 互斥鎖方式修復 package main import ( "fmt" "sync" ) func main() { done := make(chan bool) m := make(map[string]string) m["name"] = "world" var mutex sync.Mutex go func() { mutex.Lock() m["name"] = "data race" mutex.Unlock() done <- true }() mutex.Lock() fmt.Println("Hello,", m["name"]) mutex.Unlock() <-done } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看