在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 截圖測試
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強(qiáng)大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

截圖測試

開發(fā)者對于為自己的應(yīng)用寫測試有自己的動機(jī)。雖然我認(rèn)為應(yīng)該寫測試,但是這篇文章不是來勸說你來做這個的。

為一個 app 的表現(xiàn)層寫測試是一件棘手的工作。Apple 對于對象的邏輯測試已經(jīng)有內(nèi)建的支持,但是卻沒有支持測試那些界面代碼的結(jié)果。這個功能上的鴻溝實際上造成了很多開發(fā)者因為界面測試的復(fù)雜性而選擇忽視它。

當(dāng) Facebook 發(fā)布 FBSnapshotTestCaseCocoaPods 的時候,我起初還因為這個理由忽視了它, 還好我的同事沒有。

基于界面的測試意味著驗證你用戶最終看到的是不是你希望用戶看到的。測試界面可以保證不同版本,不同狀態(tài)的視圖看起來可以保持一致。界面測試可以用來提供一個高級別的測試,這涵蓋了很多相關(guān)對象的用例。

它如何運(yùn)行

FBSnapShotTestCase 將一個 UIView 或者 CALayer 的子類渲染為一個 UIImage。這個截圖被用和一個已經(jīng)保存了的截圖進(jìn)行比對,從而創(chuàng)建測試并生成測試的版本。當(dāng)測試失敗的時候,將創(chuàng)建一個失敗的測試的參考圖片,并且創(chuàng)建一個另外的圖像來表現(xiàn)兩者的不同之處。

這是一個失敗的測試的例子,原因是我們的一個 View Controller 中 gird 元素比預(yù)期的要少:

http://wiki.jikexueyuan.com/project/objc/images/15-6.png" alt="" />

它通過將 view 或者 layer 以及已經(jīng)存在的截圖渲染到兩個 CGContextRefs,并且用 C 函數(shù) memcmp() 來進(jìn)行內(nèi)存比較。這樣的比較會非???,我在一臺 MacBook Air 上生成 iPad 或者 iPhone 的全屏截圖并進(jìn)行測試,每張圖耗時在 0.013 到 0.086 秒之間。

當(dāng)配置好以后,它默認(rèn)會將參考圖片存儲到你項目的 [Project]Tests 目錄里面的一個叫 ReferenceImages 的子文件夾里。文件夾中是根據(jù)你的測試用例的類名建立的文件夾,在測試?yán)募A中是每個測試的參考圖片。當(dāng)一個測試失敗的時候,它會將失敗的結(jié)果存儲下來,另外再存儲一張這個結(jié)果和參考圖片的差異對比所生成圖片。三張圖片都會放到應(yīng)用的 tmp 目錄下,截圖測試同時會用 NSLog 在控制臺輸出一條命令,你可以用這條命令來啟動 Kaleidoscope 并進(jìn)行可視化的比較。

安裝

我們就不在這里兜圈子了:你應(yīng)該在使用 CocoaPods 吧,所以安裝僅僅需要在你的 Podfile 的測試 target 里面加入 pod "FBSnapshotTestCase"。運(yùn)行 pod install 就可以安裝這個庫了。

帶截圖的 XCTest

默認(rèn)的截圖測試需要繼承 FBSnapshotTestCase 而不是 XCTestCase,然后使用 FBSnapshotVerifyView(viewOrLayer, "optional identifier") 宏來和已經(jīng)存在的圖片驗證比較。這里的子類有一個 recordMode 的 boolean 屬性。當(dāng)設(shè)置了這個值的時候,會錄制一個新的截圖而不是把結(jié)果和參考圖片做比較。

@interface ORSnapshotTestCase : FBSnapshotTestCase
@end

@implementation ORSnapshotTestCase

- (void)testHasARedSquare
{
    // Removing this will verify instead of recording
    self.recordMode = YES;

    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 80)];
    view.backgroundColor = [UIColor redColor];
    FBSnapshotVerifyView(view, nil);
}

@end

缺點

沒有事情是完美的。讓我們談?wù)劜缓玫囊幻姘伞?/p>

  • 測試異步的代碼很困難。這是在 Cocoa 的測試中經(jīng)常出現(xiàn)的問題。我有兩個解決方案。你可以使用像 Specta 或者像 Kiwi 這樣的測試框架,它提供了多次運(yùn)行斷言直到超時或者成功。這意味著你可以給它 0.5 秒的時間運(yùn)行,同時測試可能被重復(fù)多次?;蛘撸氵€可以在開發(fā)你的應(yīng)用代碼的過程中,讓異步的代碼在做了標(biāo)記的時候同步運(yùn)行。
  • 有些組件測試起來很困難。有兩個需要注意的例子:在測試中一些 UIView 類不能在沒有 frame 的時候初始化,所以請總是給你的 view 一個 frame 來避免 <Error>: CGContextAddRect: invalid context 0x0. [..] 這樣的錯誤信息。 如果你使用了很多 Auto Layout 代碼,那么就不會那么簡單了?;?CATiledLayer 的 view 需要在 main screen 上并且在渲染瓦片 (tiles) 前被展現(xiàn)出來。它們同樣是異步渲染的。我一般為這些測試加入 兩秒等待
  • 蘋果的操作系統(tǒng)補(bǔ)丁會改變部件的渲染方式。比如 Apple 在 iOS 7.1 悄悄改變了字體微調(diào) (font hinting),所有使用到 UILabels 的截圖都需要重新錄制。
  • 每一個截圖是一個存在你倉庫里面的 PNG 文件,對我的情況來說,每個文件通常大小在 30-100kb 之間。我用 "@2x" 模式記錄了所有的測試。截圖隨著被記錄的 view 的增多而增長。

優(yōu)點

  • 我最終做到測試了。我通過對每個通過改變對象而得到的不同的 view 的狀態(tài)編寫了截圖測試。這讓我能夠讓在單次的測試中馬上看到不同狀態(tài)的變化。不用在我的 app 中進(jìn)行點擊來進(jìn)入對應(yīng)的視圖,然后改變狀態(tài)。我只需要看看 FBSnapshotTestCase 渲染的視圖,這省去了很多開發(fā)時間。
  • 截圖測試在你運(yùn)行其他測試的同時運(yùn)行,不需要作為另外的測試 scheme 進(jìn)行。它用和其他測試相同的語言書寫。它們大多數(shù)情況下可以在不將視圖顯示在屏幕上的時候運(yùn)行。
  • 截圖可以讓代碼審查變得更具體。首先是測試,它們提供一種承諾,說明將要出現(xiàn)的變化。之后是截圖,證明測試的承諾是正確的。最后是代碼庫中的變更。在這個時候,你已經(jīng)知道變化的內(nèi)容了,你不僅對內(nèi)在的改變清楚明白,也對用戶在外表上將看到的變化了然于胸。
  • 讓代碼審查可視化,可以讓設(shè)計師也參與其中。他們通過監(jiān)測項目的倉庫里面的圖片控制變化。
  • 我發(fā)現(xiàn)寫截圖測試可以提供更多的測試覆蓋度。我不相信 100% 的單元測試覆蓋度就是最優(yōu)方案。我盡量做實用的測試,測試大多數(shù)引入的變化。截圖測試在不用指定代碼路徑的時候就測試了很多代碼路徑。這是因為截圖測試可以很容易地測試一系列系統(tǒng)元素結(jié)合的結(jié)果。通過比較截圖可以方便的,你可以很快速地達(dá)到可觀的測試覆蓋率。
  • 截圖測試非???,在 Macbook Air 中使用 retina 的 iPad 尺寸的圖片平均每個測試需要運(yùn)行 0.015 到 0.080 秒。一個應(yīng)用里面運(yùn)行上百個測試都沒問題。我在開發(fā)的應(yīng)用 有數(shù)百個測試,但是它們能在 5 秒以內(nèi)運(yùn)行完畢。

工具

FBSnapShots + Specta + Expecta

我沒有使用原生的 XCTest。我用的是 Specta 和 Expecta,因為使用的時候更加簡單,可讀性也更強(qiáng)。這是你在創(chuàng)建一個新 CocoaPod 的時候的初始配置。我是 Expecta+Snapshots 這個 pod 的貢獻(xiàn)者,它為 FBSnapshotTestCase 提供了一個類似 Expecta 的 API。它會為截圖命名,同時可以在視圖的生命周期里面選擇性運(yùn)行。我的 Podfile 看起來是這樣子的:

target 'MyApp Tests', :exclusive => true do
    pod 'Specta','~> 1.0'
    pod 'Expecta', '~> 1.0'
    pod 'Expecta+Snapshots', '~> 1.0'
end

然后,我的測試看起來會是這個樣子的:

SpecBegin(ORMusicViewController)

it (@"notations in black and white look correct", ^{
    UIView *notationView = [[ORMusicNotationView alloc] initWithFrame:CGRectMake(0, 0, 80, 320)];
    notationView.style = ORMusicNotationViewStyleBlackWhite;

    expect(notationView).to.haveValidSnapshot();
});

it (@"Initial music view controller looks corrects", ^{
    id contoller = [[ORMusicViewController alloc] initWithFrame:CGRectMake(0, 0, 80, 80)];
    controller.view.frame = [UIScreen mainScreen].bounds;

    expect(controller).to.haveValidSnapshot();
});

SpecEnd

Snapshots Xcode 插件

解析 console 里面的日志來找到圖片要花不少力氣,裝載不同的失敗測試到一個可視化的工具比如 Kaleidoscope 里,需要運(yùn)行不少命令行程序。

為了處理幾乎所有這些常見的場景,我寫了一個 Xcode 插件 Snapshots。它可以通過 Alcatraz 安裝或者自己編譯。它可以讓在 Xcode 中失敗測試的失敗和成功的圖片的比較變得非常容易。

總結(jié)

FBSnapshotTestCase 給你一個測試視圖相關(guān)代碼的方法,它可以用來測試視圖相關(guān)的狀態(tài)而不用依賴于模擬器。如果你使用 Xcode 的話,你可以考慮和我的插件 Snapshots 一起使用它。有些時候它可能會讓人很煩,但是這還是值得的。它可以讓設(shè)計師參與代碼審查階段,也可以成為為現(xiàn)有項目寫測試的簡單的第一步,你可以試一試。

開源項目案例: