go 基準測試 找不到函數_基于Golang做測試

本文在實習期間完成并完善,無任何公司機密,僅做語言交流學習之用。

持續更新。


1.Golang的單元測試

Go語言提供了豐富的單測功能。在Go中,我們通常認為函數是最小的可執行單元。本例中使用兩個簡單的函數:IsOdd和IsPalindrome來進行Go單測的研究。

171457c245794c2982c3088953e5848f.png

在VSCode中,在函數名上點擊右鍵,選擇“Go: Generate Unit Tests For Function"即可生成單測文件。

2461b13ac0aa1ebea157539ad8454d1a.png

前往對應的*_test.go,開始以表格化的方式填寫測試用例。這里我們每個函數都填5個測試用例:

87616b397052867a8991987dc7abbb20.png

這里name代表的是測試用例的名字,這里建議每個測試用例的名字都唯一,否則你很有可能不知道發生錯誤的用例到底是哪個。同時我在34行和38行加入了當測試用例不通過時,輸出測試用例名字的語句。這樣就可以快速定位到測試不通過的測試用例。這里我們故意將第5個測試用例的want寫錯(32767是奇數,所以本應該返回true的,也就是want應該為true),看看測試工具是怎么報錯的。

點擊函數名上面那個run test,,這樣可以開始執行測試。run test只會運行它所下面一行所聲明的測試函數。如果需要測試所有函數可以命令行輸入go test或者go test -v。對IsOdd的測試如下:

a72705322326a66bd35c49fe31eb5dae.png

可以看到其實不自己添加用例輸出語句,FAIL的時候go的測試工具也會幫你輸出到底是哪個用例不通過。所以34行和38行的代碼并不是必須的。我們把第五個測試用例修改成正確的,再次運行測試:

140a3dfa4a67b9e9d654ce1a58239245.png

這個時候全部的測試用例就通過了,而且因為這是一種完全只是關心輸入輸出的測試,并不涉及到函數內部的具體細節,我們稱之為“黑盒測試”。

而其中我們還能選擇不同的日志等級輸出錯誤信息,單測框架提供的所有日志方法都會結束測試,若只想標記測試用例不通過,請使用t.Fail()。具體的日志方法如表:

方法功能

t.Log()/t.Logf() 打印標準等級日志,同時結束測試

t.Error()/t.Errorf() 打印錯誤等級日志,同時結束測試

t.Fatal()/t.Fatalf() 打印致命等級日志,同時結束測試

2.Golang的測試覆蓋率

Go的測試覆蓋率一般指的是測試用例可以觸發函數內的多少個分支語句占全部的分支語句的比例,在VSCode中可以以顏色區分的方式來判斷當前的測試函數覆蓋到了哪些分支,沒有覆蓋到哪些分支。首先,在測試文件中,右鍵任意一個測試函數,選擇"Go: Toggle Test Coverage In Current Package"來開始進行測試樣例覆蓋。

39ae399815d1b4e1f03f3dc1373dea27.png

這里我們將返回值應為false的測試用例給去掉,執行測試。測試完成后回到被測試的函數源文件中,可以發現被測到的分支為以墨綠色標記,而沒有被測試到的代碼分支以紅色標記。

77f5455e636f2c3d32e75ba8f6841e8a.png

將測試樣例復原后再進行測試,可以發現測試覆蓋率達到了100%,同時所有的代碼都是墨綠色的了:

1394b972807082566850f56a510492fa.png

3.Golang的基準性能測試

3.1 非并行Golang benchmark

Golang提供了測試函數運行性能的工具,對于所有的函數來說,其性能測試函數都是在前面加Benchmark。我們還是用上面所說的兩個函數,來寫一下它們的benchmark測試函數(但是我沒找到怎么一鍵生成benchmark的選項):

d921feb916e4bcc3078735bbc32cbca8.png

其中b.ReportAllocs()會報告這個函數的內存使用情況(執行一次方法要申請多少次內存,每次申請需要申請多大的內存),也可以通過指定-benchmem參數來輸出所有函數的內存性能。

執行測試命令(或者使用VSCode的那個run benchmark按鈕測試單個函數的benchmark):

Linux:
go test -bench=.
go test -bench=. -benchmem # If memory analysis info is required
go test -bench=. -benchtime=3s # If benchmark test time is not 1s, use -benchtime to set it
Windows:
go test -bench="."
go test -bench="." -benchmem # If memory analysis info is required
go test -bench="." -benchtime=3s # If benchmark test time is not 1s, use -benchtime to set it

測試出來的結果如下:

81b4ce139bfed07ebf010a8d645e31b7.png

輸出解讀:

數據意義

BenchmarkIsOdd-8 以P=8來測試IsOdd的性能

232279942 代表在1s內(如果沒有指定-benchtime則默認測試時間為1s)執行了IsOdd 232279942次

5.16 ns/op 代表每執行一次IsOdd所花費的時間為5.16 ns

0B/op 代表每執行一次IsOdd所分配的內存為0B

0 allocs/op 代表每執行一次IsOdd申請分配內存的次數為0次

我們新寫一個函數,這個函數涉及到分配內存。我們先寫一個AllocFixedArray來申請一個長度固定的數組并循環往里面填寫數據,然后再寫一個AllocMutableArray來申請一個長度可擴充的數組,使這兩個申請數組的長度相同,觀察它們的性能:

2e383b19dd4303696eb97b5d49d0ec7a.png

首先對它們做單測,確保代碼運行上沒有問題,由于這里沒有邏輯判斷,所以有一個測試用例就夠了:

5442d4e1692fef2f9a0015af15f399d9.png

確認結果正確后,寫出它們對應的Benchmark函數:

0c80664bb240707734343e2349fe5863.png

然后可以點擊run benchmark一個個測,或者直接全部函數都測一下,這里選擇全部測試:

e1d23cf8cdfcb8445544a31d7abbd31f.png

可以發現,使用make聲明固定長度的內存是沒有allocs的,而append底部會在數組長度不足的時候對數組進行擴充,所以會有內存的申請。并且我們可以看到,append因為底層申請了內存,性能大大下降,AllocMutableArray的執行時間是AllocFixedArray的差不多25倍。這個也提示我們,盡量要對數組的大小有一個預先的估計,并申請好一個capacity比較接近最大上限的數組。

3.2 并行Golang benchmark

測試的時候同樣可以使用并行的方法去并發測試指定的時間內能執行多少次該方法,其基本語法為:

b

我們試試將執行比較慢的AllocMutableArray()來并發處理,看看會如何:

822e99bde21d6c722f48c72cff1c11ea.png

執行基準測試,得到結果:

10585c6c2473519138311fba014b8661.png

我們可以發現,在P=8并發執行AllocMutableArray之后,執行時間從73.6ns/op降到了14.6ns/op。

3.3 Golang benckmark中的計時器

假設說一個函數在執行之前,要先執行一些外部的初始化操作。而我們如果在go test里面制定了-benchtime選項,它記錄的將會是整個Benchmark函數的運行時間。所以我們需要有一種操縱定時器的方法,來獲得整個服務精確的運行時間。假設我們的IsOdd,它在執行之前需要睡眠100毫秒,那么我們就可以在執行完睡眠之后,使用重置計數器的方法開始計時。

d4b66acb8a78770e0a7750d536a047d3.png

a5af15f2135762a807f58fdc7c9203a8.png

OK,測試用的總共時間為3.166s。然后我們加上不對初始化進行計時的代碼(取消16、18行的注釋),重新測一次:

2e5b0fe12ed7368c8ce620ded6174150.png

可以看到測試時間有顯著的下降,這說明使用b.StopTimer()后,沒有將初始化的時間算在總測試時間內。

方法功能

b.StartTimer() 復原或打開計時器,當Benchmark執行前會首先執行b.StartTimer()

b.StopTimer() 暫停當前計時器

b.ResetTimer() 重置當前計時器的值,go官方說該函數在計時器運行時無效,但我試了一下是有效的。建議先b.StopTimer()后再調用此方法,最后再b.StartTimer()

StartTimer, StopTimer和ResetTimer其實就相當于我們常用的秒表的三個按鈕:開始,暫停和復位。當Benchmark函數執行之前,就會自動調用StartTimer。而ResetTimer函數生效的前提是必須先調用StopTimer。通過這樣的控制,就可以控制基準測試的計時器,防止一些無關部分的時間被測算進來了。

3.4 Golang benchmark的Profile(性能分析)

golang的benchmark提供了一種輸出性能分析的工具,在測試benchmark命令的前提下,加上參數即可,下面提供了三種獲取全部基準測試函數不同性能的指令:

test -bench

當獲得這些性能文件之后,也會相應地留下一個***.test為文件名的可執行文件。為什么要留下這個測試時候生成的臨時程序呢?在生成profile文件的時候,為了減少冗余,生成的文件全部都是不含符號信息的,也就是說其實并沒有記錄性能條目對應的是哪個函數的性能,所以需要有一個這樣的副本程序來記錄符號信息。

當我們獲得這些文件之后,使用go自帶的pprof來查看這些文件所表示的含義,其中-nodecount=10表示僅顯示前10個最耗性能的條目:

=

其中***為根據實際需要所替換的字符串,一個名為go-learning的程序的cpu占用情況分析如下圖:

da18e46f45e01db6f52cfb588785f7d7.png

可以看到,IsPalindrome的占用時間排第2位,僅次于gc。所以我們可以著手去從這個函數進行優化。

4.Golang的Example測試(樣例測試)

樣例測試比較像平時在一些算法刷題平臺(比如LeetCode)的題目的一些例子,樣例測試以Example打頭,其邏輯也很簡單,就是使用fmt.Println輸出該測試用例的返回結果,然后在函數體的末尾使用如圖的注釋,一一對應每個fmt.Println的輸出:

0c5826a68c02445e512d3dcbdaa5d56d.png

17行的output首字母大寫小寫均可。

如果13~16行輸出的結果和18~21行的結果相對應,go test就會PASS,否則就會FAIL,并打印出實際輸出和期望輸出。

5.Go的Mock方法

5.1 Mock的簡介

mock,中文譯名為“模仿,假的”,顧名思義就是構建一個模擬對象,來替換掉一些需要在特定環境下觸發的服務,使其可以在不修改原服務的前提下達到測試的目的。本文介紹一種是基于gomonkey的函數/變量Mock方法。

5.2 基于gomonkey的函數Mock方法

在使用gomonkey之前,我們要先安裝它,輸入命令:

go get github.com/agiledragon/gomonkey

并在開頭import該包:

import 

假設我們有一個函數IsRest,當調用這個函數的時候,程序會判斷一下現在的時間是否已經是下午5點之后,如果是,就返回nil,表示現在是下班時間了。否則返回非nil值,表示現在還沒到下班時間。我們先寫出這個函數:

d1e4ba6228cd6b25e724b1f5e34187b5.png

那我們測試的時候肯定不可能等到5點再去測這個函數吧?否則這測試不就沒法做了。這個時候我們先生成它的單測函數,然后施加mock:

61b54abe756b11b88cd6fea80aea9009.png

其中,108行~114行是對IsRest進行mock的方法,ApplyFunc指的是對函數進行Mock,第二個參數就是要使用的Mock方法。

那我們來執行測試:

42b0589bc9e8925c039732484dda35bc.png

說明在執行測試用例的時候,gomonkey成功地把IsRest方法給mock掉了。

6. 總結

Go語言本身提供了豐富的單元測試和性能測試方法,但是在提供Mock方法上還是略有不足。本文從Gomock, Gomonkey和GoStub出發,總結了一些創建Mock對象的方法。如果對于Go測試有進一步興趣的,可以去了解GoConvey,GoMonkey,GoStub和GoMock的教程,下面列出了一個作者寫的關于這四個測試工具的文章,供讀者參考:

GoConvey框架使用指南

https://www.jianshu.com/p/633b55d73ddd?www.jianshu.com

GoMock框架使用指南

https://www.jianshu.com/p/f4e773a1b11f?www.jianshu.com

GoStub框架使用指南

https://www.jianshu.com/p/70a93a9ed186?www.jianshu.com

Monkey框架使用指南

Monkey框架使用指南?www.jianshu.com
ae644f7272f8893f28bb044a798dd64f.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374210.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374210.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374210.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

九齊NY8B072A單片機使用筆記(三)模擬串口RX

因為這款單片機沒有硬件串口,所以需要我們自己做軟件模擬串口。 用PA3作為RX,因為PA3可以作為外部輸入中斷EXTI1。 本人首先用輪詢的方式查PA3是否從高電平跳變到低電平(起始信號),但是因為還有別的業務邏輯&#xf…

Java RESTful API集成測試

這篇文章將重點介紹為RESTful API(帶有JSON有效負載)編寫Java集成測試的基本原理和機制。 目的是對技術進行介紹,并為基本正確性編寫一些測試。 這些示例將使用最新版本的GitHub REST API。 對于內部應用程序,這種測試通常將在持…

java警惕自增的陷阱

public class proposal{public static void main(String[] args) {int count0;for(int i0;i<10;i){countcount;}System.out.println(count);}}結果輸出&#xff1a;0/*步驟一&#xff1a;JMV吧count值&#xff08;其值是0&#xff09;拷貝到臨時變量區&#xff1b;步驟二:co…

[LindCode] Binary Tree Postorder Traversal

Binary Tree Postorder Traversal Given a binary tree, return the postorder traversal of its nodes values. Example Given binary tree {1,#,2,3}, 1\2/3return [3,2,1]. Challenge Can you do it without recursion? SOLUTION 1: recursion&#xff1a; 分治法解決之&am…

九齊NY8B072A單片機使用筆記(一)TIMER0定時器

先上代碼 //8bit count up , max 0xFF void Ny8b072a_Timer0_Init(void) {PCON1 C_TMR0_Dis; // Disable Timer0//1 * (255 - 5) 250usTMR0 5; // Load 0x00 to TMR0 (Initial Timer0 register)//16M 2T Div8 1usT0MD C_PS0_TMR0 | C_PS0_Div8 ; // Prescaler0 is assign…

python菜鳥教程split_Python split()方法

網頁地址解析&#xff1a; #codingutf-8 str"http://www.runoob.com/python/att-string-split.html" print("0:%s"%str.split("/")[-1]) print("1:%s"%str.split("/")[-2]) print("2:%s"%str.split("/"…

金山毒霸垃圾清理

金山毒霸-垃圾清理-單文件封裝,清潔潔癖的愛好&#xff01; 實話&#xff0c;金山的軟件確實不錯。展望金山可以在軟件行業&#xff0c;做出讓世界都使用的。為國人扛起一片天 下載地址&#xff1a; http://pan.baidu.com/s/1dFa7GdV轉載于:https://www.cnblogs.com/xiaochina/…

并發優化–減少鎖粒度

在高負載多線程應用程序中&#xff0c;性能非常重要。 開發人員必須意識到并發問題才能獲得更好的性能。 當我們需要并發時&#xff0c;我們通常擁有必須由兩個或更多線程共享的資源。 在這種情況下&#xff0c;我們有一個競爭條件 &#xff0c;其中只有一個線程&#xff08;在…

Java1.5增加了新特性:可變參數

/*Java 可變參數Java1.5增加了新特性&#xff1a;可變參數&#xff1a;適用于參數個數不確定&#xff0c;類型確定的情況&#xff0c;java把可變參數當做數組處理。注意&#xff1a;可變參數必須位于最后一項。當可變參數個數多余一個時&#xff0c;必將有一個不是最后一項&…

C語言代碼規范(十)花里胡哨代碼鑒賞

一、宏定義篇 1、作者的目的是防止GPIO口賦值超過1。但是有明顯自覺高人一等&#xff0c;瞧不起讀者的感覺。 uint8_t not_func(uint8_t sw) {return (sw?1:0); }#define LED1(sw) PA12not_func(sw)修改建議&#xff1a; #define LED1 PA12 #define LED_ON 0 #de…

python-break循環中斷

Python break語句&#xff0c;就像在C語言中&#xff0c;打破了最小封閉for或while循環。 break語句用來終止循環語句&#xff0c;即循環條件沒有False條件或者序列還沒被完全遞歸完&#xff0c;也會停止執行循環語句。 break語句用在while和for循環中。 如果您使用嵌套循環&am…

正則表達式驗證六位數以上數字,符號,字母任意兩種混合的密碼驗證策略

^(?![0-9]$)(?![a-zA-Z]$)(?!([^(0-9a-zA-Z)]|[\(\)])$)([^(0-9a-zA-Z)]|[\(\)]|[a-zA-Z]|[0-9]){6,}$這個正則如果是單獨的數字&#xff0c;字符和符號&#xff0c;是不能通過的&#xff0c;少于6位也不行&#xff0c;希望大家可以繼續驗證正確性吧轉載于:https://www.cnbl…

python post請求實例_Python使用requests發送POST請求實例代碼

本文研究的主要是Python使用requests發送POST請求的相關內容&#xff0c;具體介紹如下。 一個http請求包括三個部分&#xff0c;為別為請求行&#xff0c;請求報頭&#xff0c;消息主體&#xff0c;類似以下這樣&#xff1a; 請求行 請求報頭 消息主體 HTTP協議規定post提交的數…

Java Micro-Benchmarking:如何編寫正確的基準

幾個月前&#xff0c;我寫了一篇文章比較循環的短索引的性能 。 我問自己關于使用短褲作為循環迭代次數很少的循環的性能。 在Java語言中&#xff0c;所有對整數的操作都是int進行的。 因此&#xff0c;如果我們使用short作為循環索引&#xff0c;則在每次迭代時都將進行類型轉…

新唐M031學習筆記(一)定時器基礎計數應用

先上代碼 void Hw_Timer0_Init(void) {//20:100ms 200:10ms 2000:1ms 20000:100us 200000:10us TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 200000);/* Update prescale to set proper resolution. */TIMER_SET_PRESCALE_VALUE(TIMER0, 1); /* Enable Timer0 interrupt */TI…

java三元操作符注意

/* 三元操作符的類型務必一致 */ public class proposal_3 {public static void main(String[] args) {int i80;String sString.valueOf(i<90?90:100);String s1String.valueOf(i<90?90:100.0);if(s.equals(s1))System.out.println("s和s1相等&#xff01;"…

緩解口臭可以喝一種水

河南中醫學院第一附屬醫院耳鼻喉科主任醫師梅祥勝點評&#xff1a;通常情況下&#xff0c;口臭跟脾胃濕熱有關。中醫講&#xff1a;“胃主受納&#xff0c;脾主運化&#xff1b;胃氣主降&#xff0c;使飲食物及 其糟粕得以下行&#xff0c;脾氣主升&#xff0c;則飲食物之精華得…

asp.net+mvc+easyui+sqlite 簡單用戶系統學習之旅(二)—— easyui的簡單實用

下面開始在UserManager.Web中利用easyUI構建web。 1. 先刪除自帶的controllers、models和views&#xff08;里面的shared和web.config可以保存&#xff09;下面的文件 2. 要利用easyUI&#xff0c;首先去網上下載jquery-easyui-1.3.2.zip&#xff0c;同時下載一份EasyUI-1.3.2.…

adc如何獲取周期_LOL:千玨擁有ADC最需要的位移和無敵能力,為什么沒人用她打下路?...

— 點擊藍字 關注我們 —英雄聯盟自國服上線以來&#xff0c;已經陪伴玩家走過了9個年頭&#xff0c;目前英雄聯盟中的英雄數量已經達到了151位&#xff0c;每一位都各具特色。千玨是一位深受玩家們喜愛的英雄&#xff0c;其在官方英雄的定位中&#xff0c;屬于打野英雄&#x…

航順HK32F030MF4P6 RST作GPIO SWCLK作EXTI5 SWDIO作ADC_AIN0

老習慣&#xff0c;先上代碼 void Hw_Input_Chage_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_IOMUX, ENABLE);GPIOMUX->NRST_PIN_KEY (uint32_t)(0x00005AE1); //KEY…