在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

為單元測(cè)試“正名”

1ujk_Tencent_TE ? 來(lái)源:騰訊技術(shù)工程官方號(hào) ? 2019-12-08 11:00 ? 次閱讀

導(dǎo)語(yǔ)非常幸運(yùn)的是,從4月份至今,我能夠全身心投入到騰訊新聞的單元測(cè)試專(zhuān)項(xiàng)任務(wù)中,從無(wú)知懵懂,到不斷深入理解的過(guò)程,與開(kāi)發(fā)同學(xué)互幫互助,受益匪淺。在此過(guò)程中,得到了質(zhì)量總監(jiān)、新聞總監(jiān)和喬幫主的傾囊指導(dǎo),真心感謝!!我希望把所有心得,總結(jié)成一篇較為全面的文章,分享給其他團(tuán)隊(duì)。時(shí)刻牢記:1. 不要濫用mock 2. 基于意圖。

在我們談到單元測(cè)試,大都清楚是測(cè)試函數(shù)符合預(yù)期,國(guó)外很多大公司都將單測(cè)執(zhí)行的很好,國(guó)內(nèi)成功的案例則相對(duì)有限。在本文中,筆者將在騰訊新聞項(xiàng)目中親身經(jīng)歷單測(cè)從無(wú)到有的實(shí)踐過(guò)程梳理為可讀可參考的經(jīng)驗(yàn)分享出來(lái)。在實(shí)踐的過(guò)程我發(fā)現(xiàn),單測(cè)可以推動(dòng)產(chǎn)品質(zhì)量轉(zhuǎn)為優(yōu)秀,推動(dòng)實(shí)行它的過(guò)程更需要對(duì)它有真實(shí)的認(rèn)識(shí)以及一套方法論。

為單元測(cè)試“正名”

我曾經(jīng)認(rèn)為,單元測(cè)試面向的是一個(gè)函數(shù)。任何走出一個(gè)函數(shù)的測(cè)試,都不是單元測(cè)試。

其實(shí),對(duì)“單元”的定義取決于自己。如果你正在使用函數(shù)式編程,一個(gè)單元最有可能指的是一個(gè)函數(shù)。你的單元測(cè)試將使用不同的參數(shù)調(diào)用這個(gè)函數(shù),并斷言它返回了期待的結(jié)果;在面向?qū)ο笳Z(yǔ)言里,下至一個(gè)方法,上至一個(gè)類(lèi)都可以是一個(gè)單元(從一個(gè)單一的方法到一整個(gè)的類(lèi)都可以是一個(gè)單元)。意圖很重要(“意圖”二字是本文中第一次提到,它很重要)

我們有單元測(cè)試、增量測(cè)試、集成測(cè)試、回歸測(cè)試、冒煙測(cè)試等等,名字非常多。谷歌看到這種“百家爭(zhēng)鳴”的現(xiàn)象,創(chuàng)立了自己的命名方式,只分為小型測(cè)試、中型測(cè)試和大型測(cè)試。

小型測(cè)試,針對(duì)單個(gè)函數(shù)的測(cè)試,關(guān)注其內(nèi)部邏輯,mock所有需要的服務(wù)。小型測(cè)試帶來(lái)優(yōu)秀的代碼質(zhì)量、良好的異常處理、優(yōu)雅的錯(cuò)誤報(bào)告

中型測(cè)試,驗(yàn)證兩個(gè)或多個(gè)制定的模塊應(yīng)用之間的交互

大型測(cè)試,也被稱(chēng)為“系統(tǒng)測(cè)試”或“端到端測(cè)試”。大型測(cè)試在一個(gè)較高層次上運(yùn)行,驗(yàn)證系統(tǒng)作為一個(gè)整體是如何工作的。

資源 小型測(cè)試 中型測(cè)試 大型測(cè)試
網(wǎng)絡(luò)訪問(wèn) 僅訪問(wèn)localhost
數(shù)據(jù)庫(kù)訪問(wèn)
訪問(wèn)文件
訪問(wèn)用戶(hù)界面
使用外部服務(wù) 不鼓勵(lì),可mock
多線程
使用sleep語(yǔ)句
使用系統(tǒng)屬性設(shè)置
運(yùn)行時(shí)間限制(毫秒) 60 300 900+
強(qiáng)制時(shí)間限制(分鐘) 1 5 15
小型測(cè)試 中型測(cè)試 大型測(cè)試
對(duì)應(yīng)測(cè)試類(lèi)型 單元測(cè)試 單元測(cè)試+邏輯層測(cè)試(泛?jiǎn)卧蚍謱訙y(cè)試) UI測(cè)試或接口測(cè)試

結(jié)論:我們的單元測(cè)試,既可以針對(duì)一個(gè)函數(shù)寫(xiě)case,也可以按照函數(shù)的調(diào)用關(guān)系串起來(lái)寫(xiě)case。

金字塔模型

在金字塔模型之前,流行的是冰淇淋模型。包含了大量的手工測(cè)試、端到端的自動(dòng)化測(cè)試及少量的單元測(cè)試。造成的后果是,隨著產(chǎn)品壯大,手工回歸測(cè)試時(shí)間越來(lái)越長(zhǎng),質(zhì)量很難把控;自動(dòng)化case頻頻失敗,每一個(gè)失敗對(duì)應(yīng)著一個(gè)長(zhǎng)長(zhǎng)的函數(shù)調(diào)用,到底哪里出了問(wèn)題?單元測(cè)試少的可憐,基本沒(méi)作用。

Mike Cohn 在他的著作《Succeeding with Agile》一書(shū)中提出了“測(cè)試金字塔”這個(gè)概念。這個(gè)比喻非常形象,它讓你一眼就知道測(cè)試是需要分層的。它還告訴你每一層需要寫(xiě)多少測(cè)試。

測(cè)試金字塔本身是一條很好的經(jīng)驗(yàn)法則,我們最好記住Cohn在金字塔模型中提到的兩件事:

編寫(xiě)不同粒度的測(cè)試

層次越高,你寫(xiě)的測(cè)試應(yīng)該越少

同時(shí),我們對(duì)金字塔的理解絕不能止步于此,要進(jìn)一步理解:

我把金字塔模型理解為——冰激凌融化了。就是指,最頂部的“手工測(cè)試”理論上全部要自動(dòng)化,向下融化,優(yōu)先全部考慮融化成單元測(cè)試,單元測(cè)試覆蓋不了的 放在中間層(分層測(cè)試),再覆蓋不了的才會(huì)放到UI層。因此,UI層的case,能沒(méi)有就不要有,跑的慢還不穩(wěn)定。按照喬幫主的說(shuō)法,我不分單元測(cè)試還是分層測(cè)試,統(tǒng)一都叫自動(dòng)化測(cè)試,那就應(yīng)該把所有的自動(dòng)化case看做一個(gè)整體,case不要冗余,單元測(cè)試能覆蓋,就要把這個(gè)case從分層或ui中去掉。

越是底層的測(cè)試,牽扯到相關(guān)內(nèi)容越少,而高層測(cè)試則涉及面更廣。比如單元測(cè)試,它的關(guān)注點(diǎn)只有一個(gè)單元,而沒(méi)有其它任何東西。所以,只要一個(gè)單元寫(xiě)好了,測(cè)試就是可以通過(guò)的;而集成測(cè)試則要把好幾個(gè)單元組裝到一起才能測(cè)試,測(cè)試通過(guò)的前提條件是,所有這些單元都寫(xiě)好了,這個(gè)周期就明顯比單元測(cè)試要長(zhǎng);系統(tǒng)測(cè)試則要把整個(gè)系統(tǒng)的各個(gè)模塊都連在一起,各種數(shù)據(jù)都準(zhǔn)備好,才可能通過(guò)。

另外,因?yàn)樯婕暗降哪K過(guò)多,任何一個(gè)模塊做了調(diào)整,都有可能破壞高層測(cè)試,所以,高層測(cè)試通常是相對(duì)比較脆弱的,在實(shí)際的工作中,有些高層測(cè)試會(huì)牽扯到外部系統(tǒng),這樣一來(lái),復(fù)雜度又在不斷地提升。

為什么做單測(cè)

這個(gè)問(wèn)題我們規(guī)避不掉。新聞是這次研發(fā)模式改革的主力軍之一,所以自上而下的推動(dòng)讓這個(gè)問(wèn)題不那么棘手:做了就是做了。不做,卻又有那么多的理由:(搜集到的吐槽真實(shí)聲音)

單元測(cè)試?yán)速M(fèi)了太多的時(shí)間

單元測(cè)試僅僅是證明這些代碼做了什么

我是很棒的程序員,我是不是可以不進(jìn)行單元測(cè)試?

后面的集成測(cè)試將會(huì)抓住所有的bug

單元測(cè)試的成本效率不高我把測(cè)試都寫(xiě)了,那么測(cè)試人員做什么呢?

公司請(qǐng)我來(lái)是寫(xiě)代碼,而不是寫(xiě)測(cè)試

測(cè)試代碼的正確性,并不是我的工作

我覺(jué)得我們總監(jiān)指導(dǎo)的很到位:改革,一是工作方式的改革,更難的是思想上的改革。

單元測(cè)試的意義

新聞的總監(jiān)dot老師是至始至終推進(jìn)單測(cè)的好領(lǐng)導(dǎo),他講述了螺絲釘與飛機(jī)的故事:干貨 | 測(cè)試扁平化之必備神器:好的單元測(cè)試

單元測(cè)試對(duì)我們的產(chǎn)品質(zhì)量是非常重要的。

單元測(cè)試是所有測(cè)試中最底層的一類(lèi)測(cè)試,是第一個(gè)環(huán)節(jié),也是最重要的一個(gè)環(huán)節(jié),是唯一一次有保證能夠代碼覆蓋率達(dá)到100%的測(cè)試,是整個(gè)軟件測(cè)試過(guò)程的基礎(chǔ)和前提,單元測(cè)試防止了開(kāi)發(fā)的后期因bug過(guò)多而失控,單元測(cè)試的性?xún)r(jià)比是最好的。

據(jù)統(tǒng)計(jì),大約有80%的錯(cuò)誤是在軟件設(shè)計(jì)階段引入的,并且修正一個(gè)軟件錯(cuò)誤所需的費(fèi)用將隨著軟件生命期的進(jìn)展而上升。錯(cuò)誤發(fā)現(xiàn)的越晚,修復(fù)它的費(fèi)用就越高,而且呈指數(shù)增長(zhǎng)的趨勢(shì)。作為編碼人員,也是單元測(cè)試的主要執(zhí)行者,是唯一能夠做到生產(chǎn)出無(wú)缺陷程序這一點(diǎn)的人,其他任何人都無(wú)法做到這一點(diǎn)

代碼規(guī)范、優(yōu)化,可測(cè)試性的代碼

放心重構(gòu)

自動(dòng)化執(zhí)行three-thousand times

下面這張圖,來(lái)自微軟的統(tǒng)計(jì)數(shù)據(jù):bug在單元測(cè)試階段被發(fā)現(xiàn),平均耗時(shí)3.25小時(shí),如果漏到系統(tǒng)測(cè)試階段,要花費(fèi)11.5小時(shí)。

下面這張圖,旨在說(shuō)明兩個(gè)問(wèn)題:85%的缺陷都在代碼設(shè)計(jì)階段產(chǎn)生,而發(fā)現(xiàn)bug的階段越靠后,耗費(fèi)成本就越高,指數(shù)級(jí)別的增高。所以,在早期的單元測(cè)試就能發(fā)現(xiàn)bug,省時(shí)省力,一勞永逸,何樂(lè)而不為呢

單元測(cè)試特別耗時(shí)?

不能一刀切,不能只盯著單測(cè)階段的耗時(shí)。

我采訪了新聞客戶(hù)端、后臺(tái)的開(kāi)發(fā),首先肯定的是,單測(cè)會(huì)增加開(kāi)發(fā)量、增加開(kāi)發(fā)時(shí)長(zhǎng)。

在《單元測(cè)試的藝術(shù)》這本書(shū)提到一個(gè)案例:找了開(kāi)發(fā)能力相近的兩個(gè)團(tuán)隊(duì),同時(shí)開(kāi)發(fā)相近的需求。進(jìn)行單測(cè)的團(tuán)隊(duì)在編碼階段時(shí)長(zhǎng)增長(zhǎng)了一倍,從7天到14天,但是,這個(gè)團(tuán)隊(duì)在集成測(cè)試階段的表現(xiàn)非常順暢,bug量小,定位bug迅速等。最終的效果,整體交付時(shí)間和缺陷數(shù),均是單測(cè)團(tuán)隊(duì)最少。

單測(cè),存在即合理。一方面,需要把單測(cè)放在整個(gè)迭代周期來(lái)觀測(cè)其效果;一方面,寫(xiě)單測(cè)也是技術(shù)活,寫(xiě)得好的同學(xué),時(shí)間少代碼質(zhì)量高(也即,不是說(shuō)寫(xiě)了單測(cè),就能寫(xiě)好單測(cè))

誰(shuí)來(lái)寫(xiě)單測(cè)呢?

開(kāi)發(fā)同學(xué)寫(xiě)單測(cè)

測(cè)試同學(xué)具有寫(xiě)單測(cè)的能力。重點(diǎn)在于開(kāi)發(fā)腳手架、分層測(cè)試/端到端測(cè)試

增量還是存量

單測(cè)case針對(duì)增量代碼

當(dāng)存量代碼出現(xiàn)大規(guī)模重構(gòu),后者質(zhì)量暴露出極大風(fēng)險(xiǎn)時(shí),都是推動(dòng)補(bǔ)全單測(cè)的好時(shí)機(jī)

單元測(cè)試的階段

一. 廣義的單元測(cè)試,我們指這三部分的有機(jī)組合:

code review

靜態(tài)代碼掃描

單元測(cè)試用例編寫(xiě)

二. 結(jié)合新聞的實(shí)踐,我把單測(cè)成長(zhǎng)的過(guò)程分為4個(gè)目標(biāo),分別為:

會(huì)寫(xiě),全員可寫(xiě)

寫(xiě)的好,同時(shí)關(guān)注可測(cè)性問(wèn)題,試點(diǎn)解決

識(shí)別可測(cè)性問(wèn)題,熟練使用重構(gòu)方法進(jìn)行重構(gòu);識(shí)別代碼架構(gòu)設(shè)計(jì)問(wèn)題;case與業(yè)務(wù)代碼同步編寫(xiě)

TDD。但這個(gè)目標(biāo)是期望,不能作為必須實(shí)現(xiàn)的目標(biāo)。

截至發(fā)稿當(dāng)天,新聞處于第三階段,即,每個(gè)迭代均能產(chǎn)出高質(zhì)量的case,人數(shù)覆蓋和需求覆蓋均較高;關(guān)注重點(diǎn)在于可測(cè)性,時(shí)刻注重重構(gòu)。

單元測(cè)試的指標(biāo)

還挺尷尬的,不太有直接的指標(biāo)去衡量單測(cè)的效果。我們也經(jīng)常被問(wèn)到,“怎么證明你們新聞單測(cè)的作用呀?”

bug類(lèi)指標(biāo)(間接指標(biāo)):連續(xù)迭代的bug總數(shù)趨勢(shì)、迭代內(nèi)新建bug的趨勢(shì)、千行bug率

單測(cè)的需求覆蓋度(50%以上),參與人員覆蓋度(80%以上)

單測(cè)case總數(shù)趨勢(shì),代碼行增量趨勢(shì)

增量代碼的行覆蓋率(接入層80%,客戶(hù)端30%)

單函數(shù)圈復(fù)雜度(低于40),單函數(shù)代碼行數(shù)(低于80),掃描告警數(shù)

在迭代需求持續(xù)高吞吐量的前提下,以新聞iOS的數(shù)據(jù)為例:

go單元測(cè)試框架選型

基本選型:testify + gomonkey

附加:httptest + sqlmock

前提

測(cè)試文件,以_test.go結(jié)尾,與被測(cè)文件放于相同目錄

測(cè)試函數(shù),函數(shù)名以Test開(kāi)頭,并且隨后的第一個(gè)字符必須為大寫(xiě)字母或下劃線,如:TestParseReq_CorrectNum_TableDriven

測(cè)試函數(shù),參數(shù)為t *testing.T;對(duì)于bench測(cè)試,參數(shù)為b *testing.B

運(yùn)行命令行,我的文章有深入講解:go test命令行

testify常規(guī)用法

https://github.com/stretchr/testify

testify基于gotesting編寫(xiě),所以語(yǔ)法上、執(zhí)行命令行與go test完全兼容

支持大量高效的api,比如:

assert.Equal:常規(guī)對(duì)比,是把兩者分別換成[]byte去嚴(yán)格比對(duì) assert.Nil:判斷對(duì)象為nil時(shí),有時(shí)對(duì)err判空時(shí)也用 assert.Error:判斷err的具體類(lèi)型和內(nèi)容 assert.JSONEq:這個(gè)比較有用,對(duì)比map時(shí);或者對(duì)比struct的時(shí)候,也會(huì)先轉(zhuǎn)為map,在用這個(gè)api去做對(duì)比,如下面這個(gè)例子,我封裝了建議的方法去將struct轉(zhuǎn)換為string(json):

支持suite,用例集管理

運(yùn)行時(shí),可以指定用例集執(zhí)行

自帶mock工具,但只支持接口方法的mock,而且用法相對(duì)復(fù)雜

table-driven

gomonkey用法(加粗字體表示常用)

https://github.com/agiledragon/gomonkey

https://studygolang.com/articles/15034

支持為一個(gè)函數(shù)打一個(gè)樁

支持為一個(gè)成員方法打一個(gè)樁

支持為一個(gè)全局變量打一個(gè)樁

支持為一個(gè)函數(shù)變量打一個(gè)樁

支持為一個(gè)函數(shù)打一個(gè)特定的樁序列

支持為一個(gè)成員方法打一個(gè)特定的樁序列

支持為一個(gè)函數(shù)變量打一個(gè)特定的樁序列

table-driven的方式定義一系列stub

注意,對(duì)內(nèi)聯(lián)函數(shù)的Stub,go test命令行一定要加上參數(shù)才可生效。見(jiàn)官方文檔。所以,我的命令行默認(rèn)加上-gcflags=all=-l就行了。

我設(shè)置了一些goland的代碼模板,放在附件中。

ApplyFunc是對(duì)外部函數(shù)Stub(非類(lèi)方法)

/* 用法:gomonkey.ApplyFunc(被stub函數(shù)名, 被stub函數(shù)簽名) 函數(shù)返回值 *例子: patches := gomonkey.ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { return outputExpect, nil })*/ patches := gomonkey.ApplyFunc(lcache.GetCache, func(_ string) (interface{}, bool) { return getCommentsResp() })defer patches.Reset()

(左滑可查看完整代碼,下同)

ApplyMethod是對(duì)類(lèi)函數(shù)Stub。但這里注意,要被stub的方式是私有方法,gomonkey通過(guò)反射是找不到的,有兩種解決方法:

1)使用增強(qiáng)版的gomonkey;

2)不Stub它,而是選擇走進(jìn)這個(gè)函數(shù),這個(gè)話題在后面專(zhuān)題談mock的時(shí)候說(shuō)。

/* 用法:gomonkey.ApplyMethod(反射類(lèi)名, 被stub函數(shù)簽名) 函數(shù)返回值 *例子: var s *fake.Slice patches := ApplyMethod(reflect.TypeOf(s), "Add", func(_ *fake.Slice, _ int) error { return nil })*/ var ac *auth.AuthCheckpatches := gomonkey.ApplyMethod(reflect.TypeOf(ac), "PrepareWithHttp", func(_ *auth.AuthCheck, _ *http.Request, _ ...auth.AuthOption) error { return fmt.Errorf("prepare with nil object") })defer patches.Reset()

ApplyMethodSeq是對(duì)同一個(gè)Stub的函數(shù)返回不同的結(jié)果

/* 用法:gomonkey.ApplyMethodSeq(類(lèi)的反射,"被stub函數(shù)名", 返回結(jié)構(gòu)體); Params{info1},中括號(hào)內(nèi)為被stub函數(shù)的返回值列表; Times為生效次數(shù) *例子: e := &fake.Etcd{} info1 := "hello cpp" info2 := "hello golang" info3 := "hello gomonkey" outputs := []OutputCell{ {Values: Params{info1, nil}}, {Values: Params{info2, nil}}, {Values: Params{info3, nil}}, } patches := ApplyMethodSeq(reflect.TypeOf(e), "Retrieve", outputs) defer patches.Reset()*/conn := &redis.RedisConn{}patch1 := gomonkey.ApplyFunc(redis.NewRedisHTTP, func(serviceName string, _ string) *redis.RedisConn { conn := &redis.RedisConn{ redis.RedisConfig{}, &redis.RedisHelper{}, } return conn }) defer patch1.Reset() // mock redis data. 返回空和不為空的情況 outputCell := []gomonkey.OutputCell{ {Values: gomonkey.Params{"12", nil}, Times: 1}, {Values: gomonkey.Params{"", nil}, Times: 1}, }patchs := gomonkey.ApplyMethodSeq(reflect.TypeOf(conn.RedisHelper), "Get", outputCell)defer patchs.Reset()

先舉這幾個(gè)例子,詳細(xì)的可以在上面的鏈接文章中全面得到。

這里補(bǔ)充一點(diǎn),對(duì)類(lèi)方法進(jìn)行stub,必須要找到該方法對(duì)應(yīng)的真實(shí)的類(lèi)(結(jié)構(gòu)體),舉個(gè)例子:

//被測(cè)函數(shù)中有如下一段,其中的Get方法我們想stub掉,只要找到Get方法對(duì)應(yīng)的類(lèi)就好了readCountStr, _ := conn.Get(redisKey)if len(readCountStr) == 0 { return 0, nil }

定位conn,是RedisConn類(lèi)型的structtype RedisConn struct { RedisConfig *RedisHelper} 所以第一次,我用gomonkey.AppleyMethod時(shí)這么寫(xiě): patches := gomonkey.ApplyMethod(reflect.TypeOf(*RedisConn),"Get", func(_ *redis.RedisHelper,_ string, _ []string) ([]string, error){ return info,err_notNil })deferpatches.Reset()

運(yùn)行時(shí)報(bào)了空指針panic,提示RedisConn沒(méi)有Get方法。

繼續(xù)追,原來(lái)Get是*RedisHelper的方法,組合到了RedisConn結(jié)構(gòu)體中,共用方法。但我們使用gomonkey時(shí),需要指向真正定義它的類(lèi)

func (this *RedisHelper) Get(key string) (string, error) { return redigo.String(this.Do("GET", key))

最終這么寫(xiě):

patches := gomonkey.ApplyMethod(reflect.TypeOf(giftData.rankRedisRD.RedisHelper),"Get", func(_ *redis.RedisHelper,_ string, _ []string) ([]string, error){ return info,err_notNil })defer patches.Reset()

必須說(shuō)一說(shuō)mock了

test doubles

在《xUnit Test Patterns》一書(shū)中,作者首次提出test doubles(測(cè)試替身)的概念。我們常掛在嘴邊的mock只是其中一種,而且是最容易與Stub(打樁)混淆的一種。在上一節(jié)中對(duì)gomonkey的介紹,你可以注意到了,我沒(méi)有使用mock,全部是Stub。是的,gomonkey不是mock工具,只是一個(gè)高級(jí)打樁的工具,適配了我們大部分的使用場(chǎng)景。

測(cè)試替身,共有五種:可以參考這篇翻譯《xUnit Test Patterns》學(xué)習(xí)筆記6 - Test Double

Dummy Object:

用于傳遞給調(diào)用者但是永遠(yuǎn)不會(huì)被真實(shí)使用的對(duì)象,通常它們只是用來(lái)填滿(mǎn)參數(shù)列表

Test Stub

Stubs通常用于在測(cè)試中提供封裝好的響應(yīng),譬如有時(shí)候編程設(shè)定的并不會(huì)對(duì)所有的調(diào)用都進(jìn)行響應(yīng)。Stubs也會(huì)記錄下調(diào)用的記錄,譬如一個(gè)email gateway就是一個(gè)很好的例子,它可以用來(lái)記錄所有發(fā)送的信息或者它發(fā)送的信息的數(shù)目。簡(jiǎn)而言之,Stubs一般是對(duì)一個(gè)真實(shí)對(duì)象的封裝

Test Spy

Test Spy像一個(gè)間諜,安插在了SUT內(nèi)部,專(zhuān)門(mén)負(fù)責(zé)將SUT內(nèi)部的間接輸出(indirect outputs)傳到外部。它的特點(diǎn)是將內(nèi)部的間接輸出返回給測(cè)試案例,由測(cè)試案例進(jìn)行驗(yàn)證,Test Spy只負(fù)責(zé)獲取內(nèi)部情報(bào),并把情報(bào)發(fā)出去,不負(fù)責(zé)驗(yàn)證情報(bào)的正確性

Mock Object

針對(duì)設(shè)定好的調(diào)用方法與需要響應(yīng)的參數(shù)封裝出合適的對(duì)象

Fake Object

Fake對(duì)象常常與類(lèi)的實(shí)現(xiàn)一起起作用,但是只是為了讓其他程序能夠正常運(yùn)行,譬如內(nèi)存數(shù)據(jù)庫(kù)就是一個(gè)很好的例子。

stub與mock

打樁和mock應(yīng)該是最容易混淆的,而且習(xí)慣上我們統(tǒng)一用mock去形容模擬返回的能力,習(xí)慣成自然,也就把mock常掛在嘴邊了。

就我的理解,stub可以理解為mock的子集,mock更強(qiáng)大一些:

mock可以驗(yàn)證實(shí)現(xiàn)過(guò)程,驗(yàn)證某個(gè)函數(shù)是否被執(zhí)行,被執(zhí)行幾次

mock可以依條件生效,比如傳入特定參數(shù),才會(huì)使mock效果生效

mock可以指定返回結(jié)果

當(dāng)mock指定任何參數(shù)都返回固定的結(jié)果時(shí),它等于stub

只不過(guò),go的mock工具gomock只基于接口生效,不適合新聞、企鵝號(hào)項(xiàng)目,而gomonkey的stub覆蓋了大部分的使用場(chǎng)景。

不要濫用mock

我把這一部分單獨(dú)放一章節(jié),表現(xiàn)出它重要的意義。需要讀懂肖鵬的《mock七宗罪》,在gitchat上。

兩個(gè)門(mén)派

約從2004-2005年間,江湖上形成兩大門(mén)派:經(jīng)典測(cè)試驅(qū)動(dòng)開(kāi)發(fā)派 和 mockist(mock極端派)。

先說(shuō)mockist。他主張將被測(cè)函數(shù)所有調(diào)用的外面函數(shù),全部mock。也即,只關(guān)注被測(cè)函數(shù)自己的一行行代碼,只要調(diào)用其他函數(shù),全都mock掉,用假數(shù)據(jù)來(lái)測(cè)試。

再說(shuō)經(jīng)典測(cè)試驅(qū)動(dòng)開(kāi)發(fā)派,他們主張不要濫用mock,能不mock就不mock,被測(cè)單元也不一定是具體的一個(gè)函數(shù),可能是多個(gè)函數(shù),串起來(lái)。必要的時(shí)候再mock。

兩個(gè)門(mén)派相爭(zhēng)多年,理論各有利弊,至今仍然共存。存在即合理。比如mockist,使用了過(guò)多的mock,無(wú)法覆蓋函數(shù)接口,這部分又是很容易出錯(cuò)的;經(jīng)典派,串的太多,又被質(zhì)疑是集成測(cè)試。

對(duì)于我們實(shí)際應(yīng)用,不必強(qiáng)制遵從某一派,結(jié)合即可,需要的時(shí)候mock,盡量少mock,不用糾結(jié)。

什么時(shí)候適合mock

如果一個(gè)對(duì)象具有以下特征,比較適合使用mock對(duì)象:

該對(duì)象提供非確定的結(jié)果(比如當(dāng)前的時(shí)間或者當(dāng)前的溫度)

對(duì)象的某些狀態(tài)難以創(chuàng)建或者重現(xiàn)(比如網(wǎng)絡(luò)錯(cuò)誤或者文件讀寫(xiě)錯(cuò)誤)

對(duì)象方法上的執(zhí)行太慢(比如在測(cè)試開(kāi)始之前初始化數(shù)據(jù)庫(kù))

該對(duì)象還不存在或者其行為可能發(fā)生變化(比如測(cè)試驅(qū)動(dòng)開(kāi)發(fā)中驅(qū)動(dòng)創(chuàng)建新的類(lèi))

該對(duì)象必須包含一些專(zhuān)門(mén)為測(cè)試準(zhǔn)備的數(shù)據(jù)或者方法(后者不適用于靜態(tài)類(lèi)型的語(yǔ)言,流行的Mock框架不能為對(duì)象添加新的方法。Stub是可以的。)

因此,不要濫用mock(stub),當(dāng)被測(cè)方法中調(diào)用其他方法函數(shù),第一反應(yīng)應(yīng)該走進(jìn)去串起來(lái),而不是從根部就mock掉了。

用例設(shè)計(jì)法

喬幫主介紹了一篇文章:像機(jī)器一樣思考

文章講述思考程序設(shè)計(jì)的根本思路——考慮輸入輸出。我們?cè)O(shè)計(jì)case,想要得到最全面的設(shè)計(jì),根本是考慮全輸入全輸出的組合,當(dāng)然,一方面,這么做耗時(shí)太大,很多時(shí)候是不可執(zhí)行的;一方面,這不是想要的結(jié)果,要考慮投入產(chǎn)出比。這時(shí),需要理論與實(shí)踐相結(jié)合,理論指導(dǎo)實(shí)踐,實(shí)踐精細(xì)理論。

先說(shuō)理論

1. 還是從上篇文章說(shuō)起,考慮輸入、輸出,就要先知道哪些屬于輸入輸出:

2. 白盒&黑盒設(shè)計(jì)

白盒法:

邏輯覆蓋(語(yǔ)句、分支、條件、條件組合等)

路徑(全路徑、最小線性無(wú)關(guān)路徑)

循環(huán):結(jié)合5種場(chǎng)景(跳過(guò)循環(huán)、循環(huán)一次,循環(huán)最大次,循環(huán)m次命中、循環(huán)m次未命中)

黑盒法: 等價(jià)類(lèi):正確的,錯(cuò)誤的(合法的,非法的) 邊界法:[1,10] ==> 0,1,2,9,10,11(是等價(jià)類(lèi)的有效補(bǔ)充)

3. 結(jié)合應(yīng)用

全輸入輸出,實(shí)施難度較大,轉(zhuǎn)而我們思考到業(yè)內(nèi)大神們?cè)O(shè)計(jì)出白盒黑盒設(shè)計(jì)法,通過(guò)仔細(xì)思考,可以判斷出是對(duì)全輸入全輸出的方法論體現(xiàn)。

因此,白盒&黑盒用例設(shè)計(jì)法,每一種我都親自實(shí)踐,理解其優(yōu)缺點(diǎn),從設(shè)計(jì)覆蓋角度,條件組合>最小線性無(wú)關(guān)路徑>條件>分支>語(yǔ)句。

下面這張圖,是我早期思考用例設(shè)計(jì)時(shí)的一次實(shí)踐,現(xiàn)在回憶起來(lái),它過(guò)度設(shè)計(jì)了。

但實(shí)際中,我們擔(dān)心“過(guò)度設(shè)計(jì)”,也還無(wú)法給出答案“用什么方法設(shè)計(jì)保證萬(wàn)無(wú)一失”。

過(guò)度設(shè)計(jì),也會(huì)使case脆弱

在有限的時(shí)間內(nèi),我們尋求收益較大化

1. 小函數(shù)&重要(計(jì)算,對(duì)象處理):盡量設(shè)計(jì)全面

2. 邏輯較重,代碼行數(shù)較多:分支、語(yǔ)句覆蓋 + 循環(huán) + 典型的邊界處理(我們看個(gè)例子:GetUserGiftList)

3. 引出“基于實(shí)現(xiàn)”與“基于意圖”的設(shè)計(jì):過(guò)多去Stub被測(cè)函數(shù)內(nèi)部的調(diào)用,就越接近“基于實(shí)現(xiàn)”(第二次提到“基于意圖”)

基于意圖與基于實(shí)現(xiàn)

這個(gè)話題是非常重要的。

基于意圖:思考函數(shù)最終想做什么,把被測(cè)函數(shù)當(dāng)做黑盒,考慮其輸出輸出,而不要關(guān)注其中間是怎樣實(shí)現(xiàn)的,究竟生成了什么臨時(shí)變量,循環(huán)了幾次,有什么判斷等。

基于實(shí)現(xiàn):輸入輸出我也考慮,中間怎么實(shí)現(xiàn)的我也考慮。mock就是一個(gè)好例子,比如我們寫(xiě)一個(gè)case,我們會(huì)用mock去驗(yàn)證函數(shù)內(nèi)是否調(diào)用了哪個(gè)外部方法、調(diào)用了幾次,語(yǔ)句的執(zhí)行順序是怎樣的。程序的變動(dòng)比需求還快,重構(gòu)隨時(shí)都有,稍有一變,case大批量失敗,這也是《mock七宗罪》中提到的一種情況。

我們要的是基于意圖,遠(yuǎn)離基于實(shí)現(xiàn)。

dot老師和喬幫主給我們上了課程,結(jié)合實(shí)戰(zhàn)經(jīng)驗(yàn),我總結(jié)如下:

“要么寫(xiě)好,要么不寫(xiě)”。case也是代碼,也需要維護(hù),也有工作量,所以要寫(xiě)的到位,而不是寫(xiě)得多。寫(xiě)了一堆沒(méi)用的,你還得維護(hù),不如刪了。

拿到一個(gè)函數(shù),先問(wèn)問(wèn)自己,這個(gè)函數(shù)要實(shí)現(xiàn)什么功能,最終輸出是什么;然后,問(wèn)自己,這個(gè)函數(shù)的風(fēng)險(xiǎn)在哪里,哪部分邏輯不太自信,最容易出錯(cuò)(計(jì)算、復(fù)雜的判斷、某異常分支的命中等)。這些才是我們case要覆蓋的點(diǎn)。

內(nèi)聯(lián)函數(shù)、直接get/set,沒(méi)幾行沒(méi)什么邏輯的,只要你判斷沒(méi)什么風(fēng)險(xiǎn),就不用寫(xiě)case。

確定了要寫(xiě)的case,再用分支條件組合、邊界等核心方面設(shè)計(jì)出具體用例,實(shí)施編寫(xiě)。

可以結(jié)合新聞幾次單測(cè)case review記錄,來(lái)詳細(xì)理解。詳見(jiàn)我的KM文章

我們看一個(gè)具體的case:

拿到這個(gè)函數(shù),作為測(cè)試同學(xué)的我先向開(kāi)發(fā)了解該函數(shù)的意圖:對(duì)符合格式、符合時(shí)間的用戶(hù)禮物進(jìn)行加和

讀代碼,了解了代碼流程、幾個(gè)異常分支,先做了code review

根據(jù)必要的異常分支,設(shè)計(jì)case覆蓋

對(duì)正常的業(yè)務(wù)流程,是按照開(kāi)發(fā)講述的函數(shù)意圖,進(jìn)行設(shè)計(jì),case如下:

被測(cè)函數(shù):

ret := make(map[int]int) now := library.UnixNow() for record, numStr := range giftRecord { hasNum, err := strconv.Atoi(numStr) if err != nil || hasNum < 0 { continue } detail := strings.Split(record, ":") if len(detail) != 2 { continue } itemExpire, err := strconv.ParseInt(detail[1], 10, 64) if err != nil { continue } //星星過(guò)期 if itemExpire != 0 && now > itemExpire { continue } //統(tǒng)計(jì)可用數(shù)目 giftId, err := strconv.Atoi(detail[0]) if err != nil { continue } if _, ok := ret[giftId]; !ok { ret[giftId] = hasNum } else { ret[giftId] += hasNum }}

正常路徑的單測(cè)case

func TestNum_CorrectRet(t *testing.T) { giftRecord := map[string]string{ "1:1000": "10", "1:2001": "100", "1:999": "20", "2": "200", "a": "30", "2:1001": "20", "2:999": "200",} expectRet := map[int]int{ 1: 110, 2: 20, } var s *redis.xxx patches := gomonkey.ApplyMethod(reflect.TypeOf(s), "Getxxx", func(_ *redis.xxx, _ string)(map[string]string, error) { return giftRecord, nil })deferpatches.Reset() p := &StarData{xxx }userStarNum,err:=p.GetNum(10000) assert.Nil(t, err)assert.JSONEq(t,Calorie.StructToString(expectRet),Calorie.StructToString(userStarNum))}

有同學(xué)會(huì)問(wèn)到:但是你最終還是看的代碼呀?看到代碼的正確邏輯是怎么處理的,再去設(shè)計(jì)的case和構(gòu)造數(shù)據(jù)吧?而且你不看代碼,怎么知道有哪些異常分支要覆蓋呢?

答:1. 我現(xiàn)在作為測(cè)試同學(xué)寫(xiě)開(kāi)發(fā)同學(xué)的case,確實(shí)需要知道有哪些異常分支要處理, 但不局限于代碼中的幾種,還應(yīng)該包括我理解到的異常分支,都要體現(xiàn)在case中。我們的case絕不是為了證明代碼是怎么實(shí)現(xiàn)的!通過(guò)單測(cè),我們經(jīng)常能夠發(fā)現(xiàn)bug。但是將來(lái)是開(kāi)發(fā)來(lái)寫(xiě)單測(cè)的,他自己設(shè)計(jì)的函數(shù)肯定知道要覆蓋哪些異常分支。

2. 嗯,我需要看代碼的正常流程是怎樣的,但不代表著把代碼扒下來(lái)以設(shè)計(jì)出case。case實(shí)際上是通過(guò)與開(kāi)發(fā)的溝通后,了解輸入數(shù)據(jù)的結(jié)構(gòu),輸出的格式,數(shù)據(jù)校驗(yàn)和計(jì)算的過(guò)程,去設(shè)計(jì)輸入輸出的。

用例編寫(xiě)的策略

對(duì)于怎么個(gè)順序去寫(xiě)單測(cè),我們重點(diǎn)實(shí)踐了一番,基本上也就三種情況吧:

獨(dú)立原子:mockist,被我們推翻了。當(dāng)然,最底部的函數(shù)可能沒(méi)有外部依賴(lài),那單測(cè)它就夠了。

自上而下(紅線):從入口函數(shù)往下測(cè)。實(shí)踐的過(guò)程中,我發(fā)現(xiàn)很難執(zhí)行,因?yàn)槲覐娜肟谔幘鸵牒妹恳淮握{(diào)用都需要返回哪些數(shù)據(jù)及格式,串起來(lái)一個(gè)case已經(jīng)非常不易。

自下而上(黃線):我們發(fā)現(xiàn),入口函數(shù),往往沒(méi)什么邏輯,調(diào)用另一個(gè)函數(shù)然后拿到響應(yīng)返回。所以入口函數(shù),也許不用寫(xiě)?我們繼續(xù)往下看,每一次調(diào)用的函數(shù)都看,也調(diào)出了以往的線上線下bug,我們發(fā)現(xiàn)出現(xiàn)問(wèn)題的代碼部分往往是調(diào)用鏈的底端,尤其是涉及計(jì)算、復(fù)雜分支循環(huán)等。而且,底端的函數(shù)往往可測(cè)性較好。

因此,考慮兩方面,我們選擇自下而上設(shè)計(jì)來(lái)選擇函數(shù)編寫(xiě)case:

底部的函數(shù)可測(cè)性通常很好

核心邏輯比較多,尤其涉及計(jì)算、拼接,分支的。

可測(cè)性問(wèn)題的解決——重構(gòu)

導(dǎo)致無(wú)法寫(xiě)單測(cè)的重要原因是,代碼可測(cè)性不好。如果一個(gè)函數(shù)八九十行、二三百行,基本就是不可測(cè)的,或者說(shuō)“不好測(cè)的”。因?yàn)槔锩孢壿嬏嗔耍瑥牡谝恍械阶詈笠恍卸冀?jīng)歷了什么,各種函數(shù)調(diào)用外部依賴(lài),各種if/for,各種異常分支處理,寫(xiě)一個(gè)case的代碼行數(shù)可能是原函數(shù)的幾倍。

因此,推動(dòng)單測(cè)走下去,重構(gòu)提升可測(cè)性是必須環(huán)節(jié)。而且,通過(guò)重構(gòu),代碼結(jié)構(gòu)間接清晰了,更可讀可維護(hù),更容易發(fā)現(xiàn)和定位問(wèn)題。

常見(jiàn)的問(wèn)題:重復(fù)代碼、魔法數(shù)字、箭頭式的代碼等

推薦的理論書(shū)籍是《重構(gòu):改善既有代碼的設(shè)計(jì)》第二版、《clean code》

我輸出了一篇關(guān)于重構(gòu)的文章。

使用codecc(騰訊代碼檢查中心)的圈復(fù)雜度、函數(shù)長(zhǎng)度來(lái)評(píng)估代碼結(jié)構(gòu)質(zhì)量,我們與開(kāi)發(fā)一起學(xué)習(xí),一起實(shí)踐,不斷有成果輸出。

對(duì)于箭頭式的代碼,可考慮如下步驟:

多使用衛(wèi)語(yǔ)句,先判斷異常,異常return

將判斷語(yǔ)句抽離

將核心部分抽離為函數(shù)

用例維護(hù),可讀性、可維護(hù)性、可信賴(lài)性

用例設(shè)計(jì)要素

將內(nèi)部邏輯與外部請(qǐng)求分開(kāi)測(cè)試

對(duì)服務(wù)邊界(interface)的輸入和輸出進(jìn)行嚴(yán)格驗(yàn)證

用斷言來(lái)代替原生的報(bào)錯(cuò)函數(shù)

避免隨機(jī)結(jié)果

盡量避免斷言時(shí)間的結(jié)果

適時(shí)使用setup和teardown

測(cè)試用例之間相互隔離,不要相互影響

原子性,所有的測(cè)試只有兩種結(jié)果:成功和失敗

避免測(cè)試中的邏輯,即不該包含if、switch、for、while等

不要保護(hù)起來(lái),try…catch…

每個(gè)用例只測(cè)試一個(gè)關(guān)注點(diǎn)

少用sleep,延緩測(cè)試時(shí)長(zhǎng)的行為都是不健康的

3A策略:arrange,action,assert

用例可讀性

標(biāo)題要明確表明意圖,如Test+被測(cè)函數(shù)名+condition+result。case失敗后,通過(guò)名字就知道哪個(gè)場(chǎng)景失敗,而不用一行行再讀代碼。將來(lái)維護(hù)這個(gè)測(cè)試代碼的,可能是其他人,我們需要讓別人容易讀懂

測(cè)試代碼的內(nèi)容要清晰,3A原則:arrange,action,assert 分成三部分。數(shù)據(jù)準(zhǔn)備部分arrange如果代碼行較多,考慮抽離出去。

斷言的意圖明顯,可以考慮將魔法數(shù)字變?yōu)樽兞浚ㄋ滓淄?/p>

一個(gè)case,不要做過(guò)多的assert,要專(zhuān)一

和業(yè)務(wù)代碼的要求一致,都要可讀

用例可維護(hù)性

重復(fù):文本字符串重復(fù)、結(jié)構(gòu)重復(fù)、語(yǔ)義重復(fù)

拒絕硬編碼

基于意圖的設(shè)計(jì)。不要因?yàn)闃I(yè)務(wù)代碼重構(gòu)一次,就導(dǎo)致一批case失敗

注意代碼的各種壞味道,可參見(jiàn)《重構(gòu)》第二版

用例可信賴(lài)性

單元測(cè)試,小而且運(yùn)行快,它不是為了發(fā)現(xiàn)本次的bug,更是為了放在流水線上 努力發(fā)現(xiàn)每一次MR是否產(chǎn)生了bug。單測(cè)運(yùn)行失敗,唯一的原因只應(yīng)該是出現(xiàn)bug,而不是因?yàn)橥獠恳蕾?lài)不穩(wěn)定、基于實(shí)現(xiàn)的涉及等,長(zhǎng)期的失敗將失去單元測(cè)試的警示作用,“狼來(lái)了”的故事是慘痛的教訓(xùn)。

非被測(cè)程序缺陷,隨機(jī)失敗的case

永不失敗的case

沒(méi)有assert的case

名不副實(shí)的case

新聞單元測(cè)試的推動(dòng)過(guò)程

我們提到,對(duì)單元測(cè)試的實(shí)踐分為4個(gè)階段,每階段均有目標(biāo)。

第一階段 會(huì)寫(xiě),全員寫(xiě),不要求寫(xiě)好

由上而下的推動(dòng),從總監(jiān)到組長(zhǎng),極力支持,毫無(wú)猶豫,使組員情緒高漲

快速確定單測(cè)框架,熟練使用

結(jié)合開(kāi)發(fā)需求,輸出各場(chǎng)景下 單測(cè)框架的使用方法,包括assert、mock,table-driven等

封裝http2WebContext,方便生成context對(duì)象

多次培訓(xùn),講解單測(cè)理論及框架使用

各團(tuán)隊(duì)(終端、接入層)指定單測(cè)接口人,由他先嘗螃蟹。他是最熟悉框架使用,在前期寫(xiě)最多case的人

在磨合好單測(cè)框架的集成使用后,啟動(dòng)會(huì),部分同學(xué)先試點(diǎn)使用,確保連續(xù)兩個(gè)迭代,這幾個(gè)同學(xué)都有case輸出

每個(gè)迭代總結(jié)數(shù)據(jù)中,加入單測(cè)相關(guān)數(shù)據(jù):組長(zhǎng)和總監(jiān)非常關(guān)注單測(cè)數(shù)據(jù)信息,針對(duì)性鼓勵(lì)提升case數(shù)量和代碼行數(shù)

第二階段 寫(xiě)好,有效,全員寫(xiě)

測(cè)試同學(xué)探索出mock的正確使用方法、用例設(shè)計(jì)的正確思路,分享給團(tuán)隊(duì),經(jīng)過(guò)探討達(dá)成一致

結(jié)對(duì)編程,每迭代結(jié)對(duì)2-3個(gè)開(kāi)發(fā),共同寫(xiě)case,互相提升。

這里的結(jié)對(duì)是靈活的:有的開(kāi)發(fā),只需用半天的時(shí)間給他講框架使用,同他練習(xí),他就可以上手了不需要再擔(dān)心;有的開(kāi)發(fā),會(huì)分給測(cè)試同學(xué)需求,測(cè)試同學(xué)寫(xiě)完case后,開(kāi)發(fā)review學(xué)習(xí),并嘗試寫(xiě)出自己的第一個(gè)case;有的開(kāi)發(fā),一開(kāi)始可能不太接受,以需求不適合單測(cè)為理由,觀察了一段時(shí)間,他發(fā)現(xiàn)其他人都寫(xiě)了,也沒(méi)那么難,對(duì)團(tuán)隊(duì)也有利,他甚至?xí)鲃?dòng)找到測(cè)試同學(xué)教他寫(xiě)case。

測(cè)試同學(xué)對(duì)開(kāi)發(fā)提交的case進(jìn)行review,跟進(jìn)開(kāi)發(fā)修改后重新MR

連續(xù)兩個(gè)迭代,邀請(qǐng)dot老師、喬幫主進(jìn)行case review,效果非常好

對(duì)迭代的單測(cè)數(shù)據(jù)分析,關(guān)注需求覆蓋度、人員覆蓋度,case增量

組長(zhǎng)持續(xù)鼓勵(lì)支持單測(cè)

每迭代的需求增加“單元測(cè)試”字段,由組長(zhǎng)評(píng)估后置位。不帶單測(cè)的MR不予通過(guò),單測(cè)也要被review

第三階段 可測(cè)性提升

測(cè)試和開(kāi)發(fā)共同學(xué)習(xí)《重構(gòu)》第二版,每周有分享會(huì)

某些骨干同學(xué)優(yōu)先重構(gòu)自己的代碼

測(cè)試同學(xué)嚴(yán)格要求,先保證有單測(cè),然后小步重構(gòu),每一步均有單測(cè)保障

通過(guò)流水線的codecc掃描,圈復(fù)雜度和函數(shù)長(zhǎng)度必須達(dá)標(biāo),不可人工干預(yù)其通過(guò)

第四階段 TDD

先不保證開(kāi)發(fā)同學(xué)做到TDD,門(mén)檻還是挺高的,而且需要在線下熟練之后再運(yùn)用到業(yè)務(wù)開(kāi)發(fā)中

逐步推動(dòng)開(kāi)發(fā)將業(yè)務(wù)代碼和測(cè)試代碼同步編寫(xiě),而不是完成業(yè)務(wù)代碼后再補(bǔ)case

測(cè)試同學(xué)練成TDD

流水線

單測(cè)要放在流水線上跑,客戶(hù)端和后臺(tái)都配好了流水線,保證每次push和MR都運(yùn)行一次,發(fā)報(bào)告。

對(duì)于go的單測(cè),新聞接入層各模塊是通過(guò)MakeFile來(lái)編譯,因?yàn)橐獙?dǎo)入一些環(huán)境變量,所以我將go test集成在MakeFile中,執(zhí)行make test即可運(yùn)行該模塊下所有的測(cè)試用例。

GO = go CGO_LDFLAGS = xxxCGO_LDFLAGS += xxxCGO_LDFLAGS += xxxCGO_LDFLAGS+=xxx TARGET=aaa export CGO_LDFLAGS all:$(TARGET) $(TARGET): main.go $(GO) build -o $@ $^test: CFLAGS=-g export CFLAGS $(GO) test $(M) -v -gcflags=all=-l -coverpkg=./... -coverprofile=test.out ./...clean: rm -f $(TARGET)

注:上述做法,只能生成被測(cè)試的代碼文件的覆蓋率,無(wú)法拿到未被測(cè)試覆蓋率情況。可以在根目錄建一個(gè)空的測(cè)試文件,就能解決這個(gè)問(wèn)題,拿到全量代碼覆蓋率。

//main_test.gopackage main import ( "fmt" "testing") func TestNothing(t *testing.T) { fmt.Println("ok")}

流水線加上流程

# cd ${WORKSPACE} 可進(jìn)入當(dāng)前工作空間目錄export GOPATH=${WORKSPACE}/xxxpwd echo "====================work space"echo ${WORKSPACE}cd ${GOPATH}/srcfor file in `ls`:do if [ -d $file ] then if [[ "$file" == "a" ]] || [[ "$file" == "b" ]] || [[ "$file" == "c" ]] || [[ "$file" == "d" ]] then echo $file echo ${GOPATH}"/src/"$file cp -r ${GOPATH}/src/tools/qatesting/main_test.go ${GOPATH}/src/$file"/." cd ${GOPATH}/src/$file make test cd .. fi fidone

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4374

    瀏覽量

    64414
  • 單元測(cè)試
    +關(guān)注

    關(guān)注

    0

    文章

    50

    瀏覽量

    3292

原文標(biāo)題:從頭到腳說(shuō)單測(cè)——談?dòng)行У膯卧獪y(cè)試

文章出處:【微信號(hào):Tencent_TEG,微信公眾號(hào):騰訊技術(shù)工程官方號(hào)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    視頻教程| 單元測(cè)試工具TESSY:故障注入測(cè)試演示

    測(cè)試
    龍智DevSecOps
    發(fā)布于 :2025年05月30日 13:38:20

    新能源車(chē)軟件單元測(cè)試深度解析:自動(dòng)駕駛系統(tǒng)視角

    )的雙重標(biāo)準(zhǔn)。以制動(dòng)控制模塊例,其單元測(cè)試需實(shí)現(xiàn): ? ASIL等級(jí)分解: ?將系統(tǒng)級(jí)需求拆解到軟件單元,確保每個(gè)模塊的故障覆蓋率達(dá)標(biāo)。 ? SOTIF場(chǎng)景庫(kù): ?構(gòu)建包含數(shù)萬(wàn)個(gè)長(zhǎng)尾場(chǎng)景(如突然闖入的動(dòng)物
    發(fā)表于 05-12 15:59

    新能源車(chē)背后的隱形守護(hù)者:軟件單元測(cè)試的生死較量?

    。這個(gè)教科書(shū)級(jí)的避讓動(dòng)作背后,是超過(guò)8000萬(wàn)行代碼的精密協(xié)作,而確保這些代碼絕對(duì)可靠的秘密武器,正是我們今天要揭秘的軟件單元測(cè)試。 ?一、代碼世界的顯微鏡:單元測(cè)試為何重要? 如果把整車(chē)軟件比作一座摩天大樓,單元測(cè)試就是檢查
    的頭像 發(fā)表于 05-12 11:00 ?165次閱讀

    單元測(cè)試:構(gòu)建數(shù)字世界的質(zhì)量基石

    第一章 質(zhì)量危機(jī)時(shí)代的軟件困局 1.1?現(xiàn)代社會(huì)的軟件依賴(lài)癥候群 ? 數(shù)據(jù)支撐 ?: 世界銀行《2023全球數(shù)字基礎(chǔ)設(shè)施報(bào)告》顯示,全球關(guān)鍵基礎(chǔ)設(shè)施(如電網(wǎng)、交通、醫(yī)療)的數(shù)字化滲透率已達(dá)87%,但軟件失效導(dǎo)致的年度經(jīng)濟(jì)損失高達(dá) ? 1.2萬(wàn)億美元 ?(占全球GDP的1.4%)。 ? 失效案例技術(shù)解剖 ?: ? 波音737MAX飛控系統(tǒng) ?: cCopy Code // MCAS系統(tǒng)關(guān)鍵代碼漏洞(未驗(yàn)證傳感器失效狀態(tài)) if (sensor_data[0].valid sensor_data[1].valid) { ? ?angle = (sensor_data[0].value + s
    的頭像 發(fā)表于 04-24 10:47 ?151次閱讀

    單元測(cè)試在嵌入式軟件中的關(guān)鍵作用及winAMS工具的卓越貢獻(xiàn)

    1.?單元測(cè)試概述 ?定義與核心目標(biāo)? 單元測(cè)試是軟件開(kāi)發(fā)過(guò)程中針對(duì)程序模塊(如函數(shù)、類(lèi)或組件)的最小可測(cè)試單元進(jìn)行的驗(yàn)證活動(dòng)。其核心目標(biāo)在于隔離代碼片段,驗(yàn)證其功能是否符合設(shè)計(jì)預(yù)期,
    的頭像 發(fā)表于 04-11 14:31 ?261次閱讀

    嵌入式軟件單元測(cè)試的必要性、核心方法及工具深度解析

    一、為什么嵌入式軟件必須重視單元測(cè)試? ?嵌入式系統(tǒng)的特殊性? 在汽車(chē) ECU、醫(yī)療設(shè)備控制器等場(chǎng)景中,軟件直接操控硬件,?單比特錯(cuò)誤可能導(dǎo)致剎車(chē)失靈或呼吸機(jī)故障?。不同于 PC?軟件可頻繁熱更新
    的頭像 發(fā)表于 03-21 14:53 ?386次閱讀

    嵌入式系統(tǒng)開(kāi)發(fā)中的測(cè)試方法 嵌入式系統(tǒng)開(kāi)發(fā)與AI結(jié)合應(yīng)用

    嵌入式系統(tǒng)開(kāi)發(fā)中的測(cè)試方法 嵌入式系統(tǒng)開(kāi)發(fā)是一個(gè)復(fù)雜的過(guò)程,涉及到硬件和軟件的緊密結(jié)合。測(cè)試是確保系統(tǒng)可靠性和性能的關(guān)鍵步驟。以下是一些常用的測(cè)試方法: 單元測(cè)試
    的頭像 發(fā)表于 12-09 10:22 ?1253次閱讀

    開(kāi)發(fā)者必讀!CircleCI?組件測(cè)試單元測(cè)試全解析

    在軟件開(kāi)發(fā)中,測(cè)試是保證軟件質(zhì)量和可靠性的關(guān)鍵環(huán)節(jié)。作為領(lǐng)先的 CI/CD 平臺(tái),CircleCI 提供了支持自動(dòng)化測(cè)試的強(qiáng)大工具。其中,單元測(cè)試和組件測(cè)試是兩種重要的
    的頭像 發(fā)表于 12-03 09:18 ?592次閱讀

    汽車(chē)軟件單元測(cè)試的重要性

    測(cè)試不充分密切相關(guān),這引發(fā)了社會(huì)各界對(duì)汽車(chē)軟件健壯性的重要性進(jìn)行深入思考。本文將探討汽車(chē)軟件的測(cè)試,尤其是單元測(cè)試的重要性,以及WinAMS單元測(cè)試工具在這一過(guò)程中的關(guān)鍵作用。 一、
    的頭像 發(fā)表于 11-29 10:57 ?479次閱讀

    嚴(yán)格的單元測(cè)試造就完美的軟件

    關(guān)鍵系統(tǒng)時(shí),更是對(duì)軟件質(zhì)量提出了極高的要求。而單元測(cè)試作為軟件開(kāi)發(fā)過(guò)程中的核心環(huán)節(jié),其重要性不言而喻。 單元測(cè)試的作用 單元測(cè)試是指對(duì)軟件中的最小可測(cè)試
    的頭像 發(fā)表于 11-26 13:22 ?478次閱讀

    嵌入軟件單元/集成測(cè)試工具專(zhuān)業(yè)分析

    引言 在現(xiàn)代軟件開(kāi)發(fā)過(guò)程中,單元測(cè)試作為確保代碼質(zhì)量的重要環(huán)節(jié),得到了廣泛的關(guān)注和應(yīng)用。隨著嵌入式系統(tǒng)的復(fù)雜性日益增加,對(duì)高效、可靠的單元測(cè)試工具的需求也愈加迫切。WinAMS作為一款專(zhuān)為嵌入
    的頭像 發(fā)表于 11-19 16:41 ?616次閱讀

    Linux內(nèi)核測(cè)試技術(shù)

    。內(nèi)核測(cè)試技術(shù)是實(shí)現(xiàn)這一目標(biāo)的關(guān)鍵手段。本文將詳細(xì)介紹 Linux 內(nèi)核測(cè)試的各種技術(shù),包括單元測(cè)試、集成測(cè)試、功能測(cè)試和性能
    的頭像 發(fā)表于 08-13 13:42 ?972次閱讀
    Linux內(nèi)核<b class='flag-5'>測(cè)試</b>技術(shù)

    鴻蒙語(yǔ)言基礎(chǔ)類(lèi)庫(kù):ohos.application.testRunner TestRunner 測(cè)試

    TestRunner模塊提供了框架測(cè)試的能力。包括準(zhǔn)備單元測(cè)試環(huán)境、運(yùn)行測(cè)試用例。
    的頭像 發(fā)表于 07-12 09:32 ?565次閱讀

    單元測(cè)試、集成測(cè)試自動(dòng)化工具

    CoverageMaster winAMS :?適用于嵌入式目標(biāo)機(jī)代碼的單元測(cè)試/集成測(cè)試工具 全面支持嵌入式微機(jī)!驗(yàn)證嵌入式C/C++軟件 實(shí)施以模塊單位的自動(dòng)化單元測(cè)試工具 不需
    的頭像 發(fā)表于 06-26 13:41 ?744次閱讀
    <b class='flag-5'>單元測(cè)試</b>、集成<b class='flag-5'>測(cè)試</b>自動(dòng)化工具
    主站蜘蛛池模板: 婷婷六月天激情 | 九九热免费在线观看 | 精品人人| 成人亚洲网站www在线观看 | 影音先锋午夜资源网站 | 五月天综合婷婷 | 西西人体大胆高清啪啪欧洲 | 黄色国产网站 | 狠狠干天天操 | 亚洲va中文字幕无码 | 美女扒开尿口让男人捅 | 老师办公室高h文小说 | 天天涩综合 | 天天综合久久 | 91精品国产91久久久久青草 | 成年片色大黄全免费 | 男人操女人在线观看 | 国产亚洲高清在线精品不卡 | 夜夜操操 | 色老头成人免费综合视频 | 亚洲香蕉久久一区二区三区四区 | 欧美精品一区二区三区视频 | 国产亚洲小视频 | 久久婷婷成人综合色 | 黄色网页在线观看 | 欧美黄色片网站 | 性欧美激情在线观看 | 国产成人精品影视 | 国模私拍一区二区 | 日日爱视频 | 亚洲香蕉国产高清在线播放 | 久久精品视频7 | 性欧美网站| 三级毛片免费观看 | 777奇米影视笫四色88me久久综合 | 黄色大片网站 | 在线视频黄色 | 亚洲婷婷国产精品电影人久久 | 免费看大尺度视频在线观看 | caobi在线观看 | 国产三级国产精品 |