扣丁書屋

京東多端全流程交易解決方案阿波羅平臺iOS單元測試實踐

單元測試

什么是單元測試?

單元測試,是針對一小段代碼或方法,檢驗被測代碼一個小的、明確的功能是否正確 ,證明某段代碼的行為和開發者期望一致的行為。簡單來說,是針對單個程序、函數、過程進行正確性檢驗的工作的白盒測試。單元測試應該遵循F.I.R.S.T原則:

快速(Fast):測試應該能快速運行。

獨立(Independent)測試應該相互獨立,不能有依賴。

可重復(Repeatable)測試應當可在任何環境重復通過。包括無網絡的環境。

自足驗證(Self-Validating)測試應該有布爾值輸出。

及時(Timely)測試應及時編寫。單元測試應該恰好在使其通過的代碼之前編寫。

前端是否需要進行單元測試?

一個成熟的軟件開發人員非常清楚測試的重要性。當我們需要開發一個新功能、新需求時,我們通過單元測試來驗證一段代碼中是否運行的正常,傳入極端邊界值是否會出現問題,單元測試還能使第一次閱讀代碼的開發人員更好的理解這段代碼的含義。

經過良好的的單元測試過的代碼總會給開發人員帶來勇氣。當我們需要重新修改需求、甚至重構代碼時,因為有了良好的單元測試,即使你搞砸了,你也會很快通過運行不通過的單元測試發現問題。

所以單元測試,前端和后端都很重要,現在和以后都很重要。

單元測試現在還存在哪些問題?

開發成本增加。TDD原則要求我們在開發代碼前先編寫單元測試,按照這條規則,我們編寫的單元測試將覆蓋所有業務代碼。測試的代碼量足以匹敵需求代碼量,編寫單元測試所需要花費的時間將和開發需求的時間相同甚至遠超開發時間,這無疑增加了開發人員的開發成本。

無法驗證單元測試的有效性。基于控制唯一變量的原則,如果要驗證單元測試的有效性,我們需要找到兩個強大的團隊來執行重要的開發任務,并且在規模、結構、工具、技能水平和工作實踐——在除測試之外的所有方面的表現都大致相同。然后,還需要在一個比較長的周期內研究他們的生產力和質量差異。無疑還沒有團隊能夠這樣去做,所以我們無法使用有效的數據來說明單元測試的有效性,只能憑借以往開發者的經驗積累來進行單元測試。

接入單元測試方案,我們要如何選擇? 在阿波羅早期調研時,公司內部還沒有太多的方案可供選擇。所以我們主要是使用手動導入XCodeCoverage和OCMock庫再接入Bamboo的方案。

現在iBiu最新版本已經支持了單元測試,內部也是使用XCodeCoverage庫來生成覆蓋率。

那么就先針對這兩個平臺對現有的單測支持能力做一個對比:

Bamboo的定時觸發

方案選擇建議:

  1. 如果你現在已經接入了iBiu,但是現在不是太著急生成視圖的單測覆蓋率報告,可以先使用Xcode的自帶單測覆蓋率查看,等iBiu與Bamboo數據打通后直接通過iBiu接入。(兩個團隊已經在緊急建設中)
  2. 如果部門需要統一在Bamboo中統計單側覆蓋率,那么可以直接使用手動接入Bamboo方式,除了第一次配置略麻煩,后面直接在Bamboo定時運行還是很方便。
  3. 如果你的工程沒有接入iBiu,或者還不想接入Bamboo,但是想要生成更加詳細的單測覆蓋率,可以只接入XCodeCoverage和OCMock,生成本地單測詳細報告。

iBiu接入和Bamboo中生成單測覆蓋率報告都是使用XCodeCoverage,下文會詳細介紹這個庫的使用方法。

單元測試覆蓋率

無論在iBiu、還是Bamboo,我們關注的有效數據都是單元測試覆蓋率。

如何使用Xcode生成覆蓋率數據?

無論是哪種方式接入,單測覆蓋率最終的數據來源都是通過Xcode生成。

步驟如下:

  1. 在工程中新增單測的Target:

  2. 開啟覆蓋率需要覆蓋的范圍,選擇你需要測試target,一般是我們的組件源碼所在的target;

  3. 修改目標target的Settings

4 . 寫好單測代碼,運行command+U后,就可以看到生成的單測覆蓋率

5 . 點開詳細內容,可看到具體哪些代碼被覆蓋,其中紅色代表沒有被覆蓋,綠色代碼已經被覆蓋。 Xcode生成的單測覆蓋率報告

點擊方法后面的箭頭會跳轉到對應的代碼中

當每一次運行單測后,都應該快速的點開瀏覽所有重要的代碼塊,查看被覆蓋和沒被覆蓋的部分。往往有些你覺得單元測試已經被覆蓋到的部分,其實并沒有覆蓋到。

一般來說,單測覆蓋率不會達到100%。阿波羅前端本次接入單測只考慮業務邏輯的代碼部分,UI單測并不在本次的規劃當中,所以我們的單測覆蓋率對比服務端相對會更低一些。

如何得到更加詳細的單測覆蓋率報告?

XcodeCoverage提供了一種簡單的方法來生成Xcode項目的Objective-C代碼覆蓋率報告。生成的報告包括HTML和XML。覆蓋數據不會包括蘋果sdk,并且XcodeCoverage現在還不支持Swift。

  1. 安裝XcodeCoverage
  • 如果工程是使用iBiu,那么在Podfile文件平級的文件夾中,新增一個Podfile.custom文件,然后使用iBiu進行安裝。

文件內容:

  • 如果工程沒有使用iBiu,那就使用CocoaPods正常安裝,在Podfile文件中增加pod 'XcodeCoverage',然后pod install。

另外還需要在終端安裝兩個工具:

gem install xcpretty:

gem install ocunit2junit

xcpretty用于對xcodebuild的輸出進行格式化。并包含輸出report功能。

ocunit2junit用來將 OCUnit 的輸出轉換成 JUnit 風格的報告

2 . 配置XcodeCoverage

在你的主工程中加一個Run Script,運行一個腳本文件-

${PODS_ROOT}/XcodeCoverage/exportenv.sh

3 . exportenv.sh腳本打開一下要運行的腳本,文件就在Pod/XcodeCoverage/下

主要作用是新生成了一個env.sh文件,并寫入了BUILT_PRODUCTS_DIR、CURRENT_ARCHOBJECT_FILE_DIR_normal、OBJROOT、OBJROOT等參數。

然后我們先command+b或者command+u一下,運行這個腳本去生成env.sh。

4 . env.sh腳本 打開生成的env.sh腳本看一下

env.sh腳本中有幾個參數需要我們關注:

  • OBJECT_FILE_DIR_normal,這個值默認生成的是我們主工程的地址,但是現在我們要測試的是包含業務代碼的target,所以這個值需要修改為目標target的地址。

我們真正的代碼其實在工程中是相當于一個pod,所以這個值需要改成Pod所在的位置/Users/xxxx /Library/Developer/Xcode/DerivedData/xxxCartModule-gtprkfazpqerpsczqxftrlwkzauw/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/xxxCartModule.build/Objects-normal。主要修改是標紅的兩個位置。

  • SRCROOT,可以看到默認生成的值是指向Example文件夾的,也就是我們一般寫調用、驗證組件的等的測試代碼的地方,而我們實際的需要被單測的代碼都在classes文件中,所以這個值需要改成:/xxxCartModule/xxxCartModule/Classes。

需要注意,因為env.sh是由于run exportenv腳本每次動態生成,就會導致每次build或者跑單測都會生成一次,都需要改一次上述的兩個值。如果覺得比較麻煩可以寫一個腳本進行修改。

修改完成后我們cd到Pods/XcodeCoverage文件夾,執行getcov腳本。/getcov -s,運行后,瀏覽器會自動打開生成的單測覆蓋率詳細報告。

5 . 覆蓋率報告

生成的報告包含總代碼行、已覆蓋的代碼行、代碼覆蓋率、總方法數、已覆蓋的方法數、方法覆蓋率,可以點擊每一個類查看覆蓋的代碼和沒有覆蓋的代碼。如果想要將單測報告生成到指定文件夾,可以執行

./getcov -o 你的文件夾

生成的單測報告

文件中的詳細代碼覆蓋情況

6 . 覆蓋率報告生成原理 Xcode編譯后會為每個可執行文件生成對應的 .gcno 文件;之后在代碼中調用覆蓋率分發函數,會生成對應的 .gcda 文件。其中,.gcno 包含了代碼計數器和源碼的映射關系, .gcda 記錄了每段代碼具體的執行次數。覆蓋率解析工具都需要結合這兩個文件給出最后的檢測報表。

這就是為什么我們需要修改env.sh中的文件路徑,因為需要讓XcodeCoverage去掃描正確的gcda 文件。

剛剛在終端運行生成單測覆蓋率時的輸出記錄可以驗證以上內容。

7 . 可能遇到的報錯問題在生成覆蓋率報告的日志中,我們還會發現有類似這樣的輸出:

像我的工程輸出這個報錯是因為在代碼中使用了外部類的一個方法。

一般我們工程中會引用到許多JD內部庫,除了上面的運行錯誤日志外,還會導致無法生成最后的單測報告文件:

解決辦法是:需要讓XcodeCoverage在掃描文件的時候忽略掉JD庫,在Classes下新增一個.xcodecoverageignore文件,文件內容中寫要忽略的庫就可以了。

${SRCROOT}/報錯的庫 /*

8 . 關于getcov的其他用法

  • --show or -s: Show HTML report.
  • --xml or -x: Generate Cobertura XML. 生成覆蓋率XML
  • -o output\_dir: Specify output directory. 將結果輸出到一個文件夾
  • -i info\_file: Specify name of generated lcov info file. 指定生成的lcov信息文件的名稱
  • -v: Enable verbose output.
  • -h or --help: Show usage.

當通過以上步驟本地工程可以正常生成覆蓋率報告文件時,接入Bamboo就變得非常容易。

開發單元測試

開發測試代碼和開發需求代碼一樣重要。測試必須跟隨業務代碼的修改而修改,它不是一次性的產物,也需要開發人員進行維護。

哪些代碼需要寫單元測試?

下面表格列舉出一個工程中主要部分和是否需要單元測試,供大家參考:

寫單元測試應該在代碼開發之前,且應該自下向上進行,從最基礎的開始寫。

如何寫一個單元測試?

單元測試開發主要是基于以下三個要素:

Given:配置測試的初始狀態,比如造一些數據、mock一些對象等,這里我們可能需要OCMock這個庫的輔助。

When:對要測試的目標執行代碼,調用你要測試的方法

Then:對測試結果進行斷言(成功 or 失?。?,對于結果做一個預判,并且通過編譯來判斷你的預判是否正確。

關于單元測試開發規范,我們的建議是:

一個單測類只測一個代碼類。一個單測類中只測試這個類的相關方法,不要把所有的單測方法都寫在一個測試類中。這樣看上去擁擠不堪,既不美觀又不好維護。并且注意命名規則,可以是被測試的類名+Test。

一個單測方法只測一個方法的一種情況。我們不要想寫一個超長的測試方法,在同一個方法里測試完這個又測試那個,就像下面這種,需要拆分成兩個單測方法。當然一個方法中可以針對一種情況寫多條斷言。 ×不要這樣寫↑

如何測試代碼中的私有方法/私有屬性?

如果你需要測試定義在.m中的一個方法或者一個屬性??梢栽趩螠y的target中,創建一個Category,將私有的方法和屬性開放到.h中。并且可以將Category的.m文件刪除,它并沒有什么用。

單元測試中如何mock數據?

寫單元測試的大部分時候我們都需要mock數據舉個例子,業務代碼如下,單元測試需要驗證是否走到了if里:

業務代碼

方式1:模擬一份假數據當做入參

方式2:你當然可以不關心假數據是什么,只關注這個方法中的邏輯。那就使用OCMock直接存根一份對象。

具體選擇哪種方式可以根據被測試的方法自行判斷:如果入參是一個對象,那么選擇OCMock;如果入參是一個基礎數據,造假數據比較麻煩可以選擇OCMock,假數據結構不麻煩的話可以直接mock假數據。

單元測試如何做斷言?

需要特別關注斷言失敗的單測,這可能證明你對你的代碼有某些誤判。

蘋果提供了斷言XCTAssert等方法,具體如下:

從以上表格中可以看到蘋果提供的基本是對變量的判斷。

當我們需要校驗的是最后是否跳轉到另一個方法時,就需要使用OCMock的OCMVerify方法。

如下圖舉例: 業務代碼

單測代碼

最后讓我們聊一聊OCMock

OCMock實際是利用runtime原理,通過消息轉發方式來實現的。感興趣的同學可打開源代碼看一看。

OCMock提供了下面這些方法:

OCMock在使用上有以下幾點需要注意:

  1. OCMock生成的對象是id類型,在使用mock對象的時候,調用屬性名和方法名都不會自動帶出,所以調用的方法名需要自己寫出來。
  2. 在一個指定類上,只能有一個mock對象 *不要這樣寫↑

3 . 在使用斷言方法判斷一個方法時,如果方法有入參,可以使用[OCMArg any]當做入參,該方法可以返回一個id類型的參數,這樣無需糾結入參的值,需要注意int、float等類型不可以使用。如果在斷言方法時代碼明明走到了單測依舊報錯,可以校驗下方法中的傳參是否相同。

4 . 使用OCMock時,最好在每次使用完后手動調用stopMocking,養成用完即釋放的好習慣

以上就是OCMock經常使用的方法,建議在使用時看看官方文檔《OCMock》關于OCMock的實際用法還可以參考《OCMockTests》。


https://mp.weixin.qq.com/s/i0ABSwl-YhGnuWdbsITo4g

最多閱讀

iOS 性能檢測新方式?——AnimationHitches 7月以前  |  15985次閱讀
快速配置 Sign In with Apple 2年以前  |  5442次閱讀
APP適配iOS11 3年以前  |  4417次閱讀
App Store 審核指南[2017年最新版本] 3年以前  |  4243次閱讀
所有iPhone設備尺寸匯總 3年以前  |  4164次閱讀
使用 GPUImage 實現一個簡單相機 3年以前  |  3890次閱讀
開篇 關于iOS越獄開發 3年以前  |  3784次閱讀
在越獄的iPhone設置上使用lldb調試 3年以前  |  3708次閱讀
給數組NSMutableArray排序 3年以前  |  3634次閱讀
UITableViewCell高亮效果實現 3年以前  |  3338次閱讀
使用ssh訪問越獄iPhone的兩種方式 3年以前  |  3338次閱讀
關于Xcode不能打印崩潰日志 3年以前  |  3234次閱讀
使用ssh 訪問越獄iPhone的兩種方式 3年以前  |  3075次閱讀
為對象添加一個釋放時觸發的block 3年以前  |  2848次閱讀
使用最高權限操作iPhone手機 3年以前  |  2821次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>