Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Talking about TDD - part 1

nalydadad
November 21, 2017

Talking about TDD - part 1

This is the slide that I shared in Swift Girls Taipei.

nalydadad

November 21, 2017
Tweet

More Decks by nalydadad

Other Decks in Technology

Transcript

  1. • iOS 菜⿃鳥⼯工程師 2016/12~ • Working at • iOSDC Japan

    2017 講者 • 偶爾會出沒在 CocoaHeads Taipei About Me
  2. 需求 測試 出貨 開發 Class Class Class Class Class 單元測試

    Unit Test Unit Test Unit Test Unit Test Unit Test bug bug bug bug bug
  3. 需求 測試 出貨 開發 Class Class Class Class Class 單元測試

    Unit Test Unit Test Unit Test Unit Test Unit Test bug bug
  4. 需求 出貨 測試 bug bug 開發 Class Class Class Class

    Class 單元測試 Unit Test Unit Test Unit Test Unit Test Unit Test Unit Test Unit Test
  5. 什什麼是 TDD? • 開發的⽅方法論 • Test Driven Development • 先設計

    單元測試 項⽬目,
 再完成 Class 設計讓測試通過 需求 開發 Class 單元測試 Unit Test Unit Test Unit Test Unit Test Unit Test 在開發前,先加上單元測試
  6. TDD 開發流程 1. 分析需求,設計 Test Case 2. 開發流程: 紅燈 綠燈

    重構 • 紅燈:定義 Test case • 綠燈:設計 Class 滿⾜足測項需求 • 重構:跑 Acceptance Test,確認測項都綠燈並重構程式碼
  7. FA I L S U C C E S S

    (1)寫⼀一條測試 (2)使測試通過 (3)重構: 1. 確認其他測項正常 2. 重構程式碼 重覆此 3 步驟
  8. ⼩小結 • TDD 中,是先寫測試,在開發 • ⼀一條 case 定義好,先滿⾜足它,再繼續往下開發 • 使⽤用

    Xcode 功能,將 TDD 融入⾃自⼰己的開發流程 • ⼩小步向前,才不會跌倒
  9. 來來談談 Unit Test • 介紹 • 設計 Unit Test 的

    3A Rules • 驗證⽅方法 • 在 Xcode 使⽤用 XCTest • 產⽣生所有測試
  10. Unit Test 介紹 • 測試 “程式碼最⼩小的單元” • 以 Class 為⽬目標,測試其

    Interface • 測試項⽬目必須各⾃自獨立 • 測試程式必須儘量量簡單
  11. 3A Rule Arrange: 建立測試前的環境 Act:呼叫受測⽅方法 Assert:驗證結果 func testInputANumber() { //

    Arrange let calculator = DDCalculatorModel() // Act calculator.input(key: "1") // Assert XCTAssertTrue(calculator.currentOutput == "1") }
  12. 狀狀態 • Arrange:⼀一台⾞車車設定 10 公升的油 • Act:開了了10 km • Assert:驗證是否剩餘

    9 公升的油 測試項⽬目:驗證⼀一台⾞車車的油耗狀狀態,其平均油耗為 10km/L。
  13. import XCTest class NewTests: XCTestCase { override func setUp() {

    super.setUp() } override func tearDown() { super.tearDown() } func testExample() { XCTAssert(1 == 1, "wrong message") XCTAssertNil(nil, "wrong message") XCTAssertNotNil("Any", "wrong message") XCTAssertTrue(true, "wrong message") XCTAssertFalse(false, "wrong message") XCTAssertEqual(1, 1, "wrong message") XCTAssertGreaterThan(1, 0, "wrong message") XCTAssertLessThan(0, 1, "wrong message") XCTAssertThrowsError(CalculatorError.unSupportedOperator, "wrong message") XCTAssertNoThrow(CalculatorError.unSupportedOperator, "wrong message") } } 在 Xcode 使⽤用 XCTest 繼承 XCTest Case,class name 需以 Tests 結尾 每⼀一條 test case 測完後要做的事 test case 需以 test 開頭 每⼀一條 test case 開始測前要做的事 + U
  14. 產⽣生所有測試案例例 TDD - STEP 1 分析需求,設計 Test Case。 挑戰 1

    測試項⽬目列列到什什麼程度,才算是完整的測試? 挑戰 2
  15. • 開始問問題。 • When • Why • How • Who

    • What • Where 你會如何測試你的 相機
  16. 設計 Test Case 必須想到什什麼 Who will use it? And why?

    What are the use cases? What are the bounds of use? What are the stress/failure conditions? How would you perform the testing?
  17. Who will use it? And why? 誰會使⽤用?為什什麼? • 使⽤用對象?攝影師,⼀一般使⽤用者,⼩小朋友,老⼈人 •

    不同年年齡層,不同⾓角⾊色的使⽤用⽅方式有沒有不同?沒有 你會如何測試你的 相機 • 哪⼀一種相機?⼀一般數位相機,單眼相機,⼿手機相機
  18. How would you test a camera? What are the use

    cases? 使⽤用情境有哪些? • 有哪幾種使⽤用模式?⼿手動模式、快⾨門優先、光圈優先、⾃自動模式 • 有沒有 Plug - in?鏡頭、腳架、閃光燈 • 如何設定 • 刪除照片,傳輸檔案... • …
  19. What are the bounds of use? 使⽤用的邊界是什什麼? • 電池最⾼高可以使⽤用多久? •

    快⾨門最多可以按幾次? • 可以⼀一直開關機嗎? • 最多可以存幾張照片? • 在過冷或過熱的環境可以使⽤用嗎? 你會如何測試你的 相機
  20. What are the stress/failure conditions? 確認使⽤用情境,哪些失敗情境是可以被接受的? • 快⾨門按了了 10000 次後失效

    • 電池充電 10000 次後,開始不能充電 • 在過低溫度環境使⽤用,耗電量量過快 你會如何測試你的 相機
  21. How would you perform the testing? 定義測試⽅方法? • 驗證回傳值、狀狀態 or

    互動 • 定義如何測試個模組狀狀態 • 定義如何測試使⽤用情境 • 定義如何測試極端情境 • 確認可以被接受的使⽤用極限 你會如何測試你的 相機
  22. ⼩小結 • 如何寫 Unit Test? • Unit Test 是開發的 SPEC,根據需求分析測項

    • Unit Test 的完整性,思考如何寫出夠具說服⼒力力的 Test Suite。 • TDD 彰顯了了先寫測試的好處,清楚的 Unit Test,穩定的開發
  23. func testAsyncTask() { let promise = expectation(description: "Asnyc test complete")

    let fakeAsyncTaskManager = AsyncTaskManager() fakeAsyncTaskManager.doSomething { result, error in if let error = error { XCTFail("Error: \(error.localizedDescription)") } else { if result == .success { promise.fulfill() } else { XCTFail("Job fail, result code: \(result.rawValue)") } } } waitForExpectations(timeout: 5, handler: nil) } Asynchronous Callback
  24. func testAsyncTask() { let promise = expectation(description: "Asnyc test complete")

    let fakeAsyncTaskManager = AsyncTaskManager() fakeAsyncTaskManager.doSomething { result, error in if let error = error { XCTFail("Error: \(error.localizedDescription)") } else { if result == .success { promise.fulfill() } else { XCTFail("Job fail, result code: \(result.rawValue)") } } } waitForExpectations(timeout: 5, handler: nil) } Asynchronous Callback 1. expectation 2. wait for fulfill
  25. func testAsyncTask() { let promise = expectation(description: "Asnyc test complete")

    let fakeAsyncTaskManager = AsyncTaskManager() fakeAsyncTaskManager.doSomething { result, error in if let error = error { XCTFail("Error: \(error.localizedDescription)") } else { if result == .success { promise.fulfill() } else { XCTFail("Job fail, result code: \(result.rawValue)") } } } waitForExpectations(timeout: 5, handler: nil) } Asynchronous Callback 3. fulfill
  26. View Controller - Unit Test func testViewController() { //Arrange let

    storyboard = UIStoryboard(name: "Main", bundle: nil) let button = UIButton() let viewControllerUnderTest = storyboard.instantiateViewController(withIdentifier: "ViewController") as! MyViewController button.addTarget(viewControllerUnderTest, action: #selector(viewControllerUnderTest.didTapButton(_:)), for: .touchUpInside) //Act button.sendActions(for: .touchUpInside) //Assert XCTAssertTrue(viewControllerUnderTest.title == "Pressed") }
  27. View Controller - Unit Test func testViewController() { //Arrange let

    storyboard = UIStoryboard(name: "Main", bundle: nil) let button = UIButton() let viewControllerUnderTest = storyboard.instantiateViewController(withIdentifier: "ViewController") as! MyViewController button.addTarget(viewControllerUnderTest, action: #selector(viewControllerUnderTest.didTapButton(_:)), for: .touchUpInside) //Act button.sendActions(for: .touchUpInside) //Assert XCTAssertTrue(viewControllerUnderTest.title == "Pressed") }
  28. View Controller - Unit Test func testViewController() { //Arrange let

    storyboard = UIStoryboard(name: "Main", bundle: nil) let button = UIButton() let viewControllerUnderTest = storyboard.instantiateViewController(withIdentifier: "ViewController") as! MyViewController button.addTarget(viewControllerUnderTest, action: #selector(viewControllerUnderTest.didTapButton(_:)), for: .touchUpInside) //Act button.sendActions(for: .touchUpInside) //Assert XCTAssertTrue(viewControllerUnderTest.title == "Pressed") }
  29. View Controller - UI ⾃自動化測試 func testUI() { //Arrange XCUIApplication().launch()

    //Act XCUIApplication().buttons[“button"].tap() //Assert XCTAssertTrue(XCUIApplication().title == "Pressed") } • UITests Bundle • XCUIApplication
  30. Fake, Mock, Stub • 模擬互動⾏行行為 1. 當 Class 有 2.

    當受測⾏行行為需要很長的執⾏行行時間時,ex: URLSession 3. 當受測物件使⽤用外部 library 時,ex: NSUserDefault 外部相依性
  31. Mock Example class MockTimeMachine: TimeMachine { var timeTravelWasCalled = false

    func timeTravelTo(year: Int) { timeTravelWasCalled = true } }
  32. 相關資源 • 91 哥 - 30 天快速上⼿手 TDD • 立堯

    - Medium • Test-Driven iOS Development • Document - XCTest Framework • Cracking the coding interview