[iOS翻譯]《iOS7 by Tutorials》系列:在Xcode 5裡使用單元測試(下)

來源:互聯網
上載者:User

4.測試失敗的調試

是時候追蹤之前測試失敗的問題了。開啟GameBoard.m,找到cellStateAtColumn:andRow: 和 setCellState:forColumn:andRow: 方法,你會看到它們都調用了一個叫做checkBoundsForColumn:andRow: 的helper方法,用來檢測數組邊界。

標頭檔 GameBoard.h 裡的方法注釋如下:

// raises an NSRangeException if the column or row are out of bounds

然而,如果超出邊界,checkBoundsForColumn:andRow: 方法的實現拋出了一個NSGenericExpression 。通常的,你把標頭檔裡的注釋當做一個公用API規範,但在這個例子裡代碼和規範並不匹配,你該如何做?

 一個可能性是更新注釋和相關的測試,以匹配當前的實現。在這個例子裡,規範裡的注釋看起來更有意義:一個邊界檢查應當遵循NSArray,並升起一個NSRangeException。

 

在GameBoard.m裡,更新checkBoundsForColumn:andRow: 方法的實現如下:

- (void)checkBoundsForColumn:(NSInteger)column andRow:(NSInteger)row{if (column < 0 || column > 7 || row < 0 || row > 7)[NSException raise:NSRangeExceptionformat:@"row or column out of bounds"];}

重新運行測試,這時所有測試應該能夠通過了。

自從代碼不同步,注釋裡的規範總是有一點危險。然而,你的測試可以為注釋添加雙保險。自從你編寫測試代碼後,只要你經常運行測試,這些實現不匹配的風險會大大減小!

另外,你的測試提供了一個偉大的高層概覽代碼,特別是遵循建議的命名規範以後。當你重新進入很久沒碰過的代碼後,這會非常方便——正如下一小節的內容。

 

5.保證測試bug

一個崩潰報告剛剛進入你的app:你的一個測試人員報告你,當她運行遊戲後直接點擊螢幕(不點擊"2 Player"或者"vs computer"按鈕),程式就會崩潰。

你自己試一遍,就會在控制台看到下列資訊:

ReversiGame[1842:a0b] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'row or column out of bounds'

崩潰看起來重複發生,是什麼拋出了一個NSRangeException ?call stack提供了以下資訊:

2 CoreFoundation +[NSException raise:format:] + 1393 ReversiGame -[GameBoard checkBoundsForColumn:andRow:] + 142 4 ReversiGame -[GameBoard cellStateAtColumn:andRow:] + 765 ReversiGame -[ReversiBoard flipOpponentCountersForColumn: andRow:withNavigationFunction:toState:] + 2816 ReversiGame -[ReversiBoard makeMoveToColumn:andRow:] + 245 7 ReversiGame -[BoardSquare cellTapped:] + 192

從下往上讀:

第7、6行  tap觸發代碼用來處理玩家的移動

第5行 遊戲邏輯檢查是否有對手的棋子被包圍並且翻轉

第4、3行 代碼然後調用cellStateAtColumn:andRow: 和

checkBoundsForColumn:andRow:

第2行 底層架構報出一個越界異常

 

想瞭解更多調試崩潰的資訊?看這裡

My App Crashed, Now What?http://www.raywenderlich.com/10209/my-app-crashed-now-what-part-1Demystifying iOS Application Crash Logshttp://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs

這是一個測試這些崩潰條件的絕好機會。

你的新測試不止要修複這個問題,而且要作為一個迴歸測試確保這個bug保持修複。沒有什麼比修複一個bug後,數月之後添加新功能或重構代碼時再遇到相同的bug更讓人不爽的了。

 

6.決定什麼需要測試

你知道你需要測試——但應該測試什嗎?

ReversiBoard 是GameBoard類的通用實現,所以從這裡開始故障排除工作是有意義的。

 

使用iOS\Cocoa Touch\Objective-C test case class 模板建立一個新類,命名為ReversiBoardTests, 繼承自XCtestCase。

在開始之前,刪除模板檔案的testExample方法,然後在ReversiBoardsTests.m 裡匯入標頭檔:

#import "ReversiBoard.h"

 

ReversiBoardsTests.m 裡改變@interface 如下:

@interface ReversiBoardTests : XCTestCase { ReversiBoard *_reversiBoard;}

添加一個_reversiBoard 執行個體變數意味著你不用在每個測試方法裡反覆執行個體化。

然後修改setUp方法如下:

- (void)setUp {[super setUp];_reversiBoard = [[ReversiBoard alloc] init];}

 

7.測試否定

在之前的編寫的測試中,異常的存在是預期的結果。這一次,異常並沒有在你的測試基礎上出現。

添加這些方法到ReversiBoardsTests.m 

- (void)test_makeMove_inPreGameState_nothingHappens {[_reversiBoard setToPreGameState];XCTAssertNoThrowSpecificNamed([_reversiBoard makeMoveToColumn:3 andRow:3],NSException,NSRangeException,@"Making a move in the pre-game state should do nothing");}

上面的代碼中,測試設定遊戲前的狀態。也就是說,玩家作出對戰AI還是對戰其他玩家選擇之前的狀態。這個測試類比了一進入遊戲就點擊棋盤的動作。

XCTAssertNoThrowSpecificNamed 斷言與XCTAssertThrowsSpecificNamed 剛好相反。如果指定的異常被拋出,上面的測試會失敗;如果指定的異常沒被拋出,測試會通過。

你還沒有修複bug,所以現在運行代碼將會失敗——不過這是件好事,在修複bug之前編寫測試意味著你擁有重現bug的測試能力。

 

Command+U 運行測試,你會看到如下資訊:

test failure: -[ReversiBoardTests test_makeMove_inPreGameState_nothingHappens] failed: (([_reversiBoard makeMoveToColumn:3 andRow:3]) does not throw <NSException, "NSRangeException">) failed

 

8.校正代碼

開啟 ReversiBoard.m 然後找到 makeMoveToColumn:andRow: 方法。

思考一下如何修正這個特定的bug。只有使用者選擇了遊戲模式之後才可以移動,這是很有意義的。這樣一想,遊戲前和遊戲後的遊戲邏輯沒有什麼不同。

幸運的是,這裡有一個屬性指定當前的遊戲狀態:gameState.

 

添加以下代碼到makeMoveToColumn:andRow: 方法的頂部:

if ([self gameState] != GameStateOn) return;

這個條件檢測了當前的遊戲狀態。如果狀態不是GameStateOn——說明遊戲不在運行中——方法立即終止。

運行app,測試一下在選擇遊戲模式之前點擊棋盤,是否崩潰?

 

最後,Command+U 運行測試,Test Navigator應該顯示綠色小勾,bug被碾碎了!

探索風格的測試只用包含明顯問題的代碼,然而迴歸風格的測試則可以為經常修複某個問題提供了保障。

修複每個bug不止是讓你的代碼更健康,同時讓你有更多時間思考你的單元測試。

 

 

三、下一步何去何從?

測試是開發生涯的一個巨大任務,這章我們掌握了單元測試的基礎,下面是一些有益的概念:

  • 使用哪一個斷言?在哪裡使用斷言?
  • 添加測試到一個現有app
  • 在程式說明裡使用測試
  • 探尋並修複bug
  • 確保已經修複過的bug不再出現

 

Xcode中整合的XCTest讓建立測試套件非常容易,整個iOS領域的測試範圍是非常廣大的,更多測試概念:

  • Mock objects
    • 在測試裡類比出足夠真實的對象
      • http://ocmock.org/
  • UI testing
    • 可以類比出使用者的輸入,比如touch或文本輸入。
  • Continuous integration (CI) systems
    • 將會自動運行單元測試,想瞭解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.