Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Talking about TDD - part 1
Search
nalydadad
November 21, 2017
Technology
0
1.9k
Talking about TDD - part 1
This is the slide that I shared in Swift Girls Taipei.
nalydadad
November 21, 2017
Tweet
Share
More Decks by nalydadad
See All by nalydadad
Siri Shortcut Introduction
nalydadad
0
300
Unit Testing and Travis CI
nalydadad
0
430
Experience of iOSDC Japan 2017
nalydadad
0
37
iOSDC 2017 - Implementing Music Playback on watchOS
nalydadad
0
200
Other Decks in Technology
See All in Technology
QAエンジニアが伝えたい品質保証の羅針盤 / Compass for Quality Assurance
mii3king
1
320
kcp: Kubernetes APIs Are All You Need #techfeed_live / TechFeed Experts Night 28th
ytaka23
1
180
#phpconkagawa レガシーコードにもオブザーバビリティを 〜少しずつ始めるサービス監視〜
yamato_sorariku
0
510
Oracle Base Database Service 技術詳細
oracle4engineer
PRO
5
38k
TiDBにおけるテーブル設計と最適化の事例
cygames
0
770
Databricksの生成AI戦略
taka_aki
1
350
Kaggleで学ぶ系列データのための深層学習モデリング
yu4u
7
1.7k
PHP 9 に備えよ - 動的プロパティ、どうすればいぃ?
taisukearase
0
150
Google Cloud Next '24 Recap in ZOZO AIにより変わる開発 運用/Development and operation changed by AI
gachimuchiengineer
0
190
拓展QA日常工作的邊界
line_developers_tw
PRO
0
510
Shinagile 2024
kawaguti
PRO
2
120
AI JIMY - 登壇(インストール編)
hanacchi
0
150
Featured
See All Featured
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
352
28k
Building Your Own Lightsaber
phodgson
100
5.7k
Fashionably flexible responsive web design (full day workshop)
malarkey
398
65k
Agile that works and the tools we love
rasmusluckow
325
20k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
117
18k
The Illustrated Children's Guide to Kubernetes
chrisshort
32
47k
How to train your dragon (web standard)
notwaldorf
75
5.2k
Docker and Python
trallard
35
2.7k
Build your cross-platform service in a week with App Engine
jlugia
226
17k
Fantastic passwords and where to find them - at NoRuKo
philnash
39
2.5k
Producing Creativity
orderedlist
PRO
338
39k
How GitHub (no longer) Works
holman
305
140k
Transcript
淺談 TDD 提升程式碼品質 Dada Chen 2017/11/20
• iOS 菜⿃鳥⼯工程師 2016/12~ • Working at • iOSDC Japan
2017 講者 • 偶爾會出沒在 CocoaHeads Taipei About Me
Outline • 來來談談 TDD • 來來談談 Unit Test • ⼀一些測試技巧
開發流程 談談我們平常都是怎麼開始的...
需求 開發 測試 出貨
需求 開發 測試 出貨 class class class class class
需求 開發 測試 出貨 class class class class class
需求 測試 出貨 bug bug bug bug bug 開發 class
class class class class
你是否也是這樣? !"
今天我們⽤用 Bug 的數量量來來衡量量程式碼品質 Q:什什麼是 Bug? Q:如何減少 Bug? 如何盡量量符合預期? 思考:提升程式碼品質 Q:你覺得你寫的程式碼品質好嗎?
程式碼不符合預期 開發者的預期、spec 的預期、使⽤用者的預期 增加測試 Q:誰的預期?
在開發階段的測試 單元測試 Unit Test
需求 測試 出貨 開發 Class Class Class Class Class 單元測試
Unit Test Unit Test Unit Test Unit Test Unit Test bug bug bug bug bug
需求 測試 出貨 開發 Class Class Class Class Class 單元測試
Unit Test Unit Test Unit Test Unit Test Unit Test bug bug
需求 出貨 測試 bug bug 開發 Class Class Class Class
Class 單元測試 Unit Test Unit Test Unit Test Unit Test Unit Test Unit Test Unit Test
What’s the different? 只有少數的 Bug 要修. 到處都是 Bug. bug bug
bug bug bug bug bug bug bug Fika Time
如果你關⼼心程式碼品質 Unit Test 是 必須的!
問題: 我們什什麼時候加單元測試? 開發之前 or 開發之後 如果你關⼼心程式碼品質 Unit Test 是 必須的!
來來談談 TDD 將 Unit Test 導入 開發流程
什什麼是 TDD? • 開發的⽅方法論 • Test Driven Development • 先設計
單元測試 項⽬目, 再完成 Class 設計讓測試通過 需求 開發 Class 單元測試 Unit Test Unit Test Unit Test Unit Test Unit Test 在開發前,先加上單元測試
TDD 開發流程 1. 分析需求,設計 Test Case 2. 開發流程: 紅燈 綠燈
重構 • 紅燈:定義 Test case • 綠燈:設計 Class 滿⾜足測項需求 • 重構:跑 Acceptance Test,確認測項都綠燈並重構程式碼
FA I L S U C C E S S
(1)寫⼀一條測試 (2)使測試通過 (3)重構: 1. 確認其他測項正常 2. 重構程式碼 重覆此 3 步驟
Add Test Cases Class Red Green
⼩小結 • TDD 中,是先寫測試,在開發 • ⼀一條 case 定義好,先滿⾜足它,再繼續往下開發 • 使⽤用
Xcode 功能,將 TDD 融入⾃自⼰己的開發流程 • ⼩小步向前,才不會跌倒
要測什什麼才是最難想的 來來談談 Unit Test
來來談談 Unit Test • 介紹 • 設計 Unit Test 的
3A Rules • 驗證⽅方法 • 在 Xcode 使⽤用 XCTest • 產⽣生所有測試
Unit Test 介紹 • 測試 “程式碼最⼩小的單元” • 以 Class 為⽬目標,測試其
Interface • 測試項⽬目必須各⾃自獨立 • 測試程式必須儘量量簡單
3A Rule Arrange: 建立測試前的環境 Act:呼叫受測⽅方法 Assert:驗證結果 func testInputANumber() { //
Arrange let calculator = DDCalculatorModel() // Act calculator.input(key: "1") // Assert XCTAssertTrue(calculator.currentOutput == "1") }
驗證⽅方法 • 回傳值 • 狀狀態 • 互動
回傳值 • Arrange:⽣生成⼀一個登入系統 • Act:輸入帳號密碼 • Assert:驗證是否成功登入 測試項⽬目:驗證登入系統的登入功能
狀狀態 • Arrange:⼀一台⾞車車設定 10 公升的油 • Act:開了了10 km • Assert:驗證是否剩餘
9 公升的油 測試項⽬目:驗證⼀一台⾞車車的油耗狀狀態,其平均油耗為 10km/L。
互動 • Arrange:顧客帶了了200 元,走進麥當勞 • Act:向店員點了了⼀一號餐 • Assert:驗證顧客是否剩餘 80 元
測試項⽬目:驗證顧客⾄至麥當勞消費的⾏行行為
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
產⽣生所有測試案例例 TDD - STEP 1 分析需求,設計 Test Case。 挑戰 1
測試項⽬目列列到什什麼程度,才算是完整的測試? 挑戰 2
現在需要開發⼀一台 相機 練習
• 先分析結構。 你會如何測試你的 相機 • 照相模組 • 相片管理理模組 • 設定模組
• 開始問問題。 • When • Why • How • Who
• What • Where 你會如何測試你的 相機
設計 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?
Who will use it? And why? 誰會使⽤用?為什什麼? • 使⽤用對象?攝影師,⼀一般使⽤用者,⼩小朋友,老⼈人 •
不同年年齡層,不同⾓角⾊色的使⽤用⽅方式有沒有不同?沒有 你會如何測試你的 相機 • 哪⼀一種相機?⼀一般數位相機,單眼相機,⼿手機相機
How would you test a camera? What are the use
cases? 使⽤用情境有哪些? • 有哪幾種使⽤用模式?⼿手動模式、快⾨門優先、光圈優先、⾃自動模式 • 有沒有 Plug - in?鏡頭、腳架、閃光燈 • 如何設定 • 刪除照片,傳輸檔案... • …
What are the bounds of use? 使⽤用的邊界是什什麼? • 電池最⾼高可以使⽤用多久? •
快⾨門最多可以按幾次? • 可以⼀一直開關機嗎? • 最多可以存幾張照片? • 在過冷或過熱的環境可以使⽤用嗎? 你會如何測試你的 相機
What are the stress/failure conditions? 確認使⽤用情境,哪些失敗情境是可以被接受的? • 快⾨門按了了 10000 次後失效
• 電池充電 10000 次後,開始不能充電 • 在過低溫度環境使⽤用,耗電量量過快 你會如何測試你的 相機
How would you perform the testing? 定義測試⽅方法? • 驗證回傳值、狀狀態 or
互動 • 定義如何測試個模組狀狀態 • 定義如何測試使⽤用情境 • 定義如何測試極端情境 • 確認可以被接受的使⽤用極限 你會如何測試你的 相機
從需求分析模組 從問題分析使⽤用情境 從邊界值考慮更更多測項 思考測項的必要性 產⽣生所有測試案例例 ⼀一般⾏行行為的測項 模組屬性的測項 思路路 產⽣生測試項⽬目
⼩小結 • 如何寫 Unit Test? • Unit Test 是開發的 SPEC,根據需求分析測項
• Unit Test 的完整性,思考如何寫出夠具說服⼒力力的 Test Suite。 • TDD 彰顯了了先寫測試的好處,清楚的 Unit Test,穩定的開發
Practice
使⽤用 TDD 開發計算機 Calculator input output @GitHub: nalydadad
Unit Test 技巧
Fake, Mock, Stub View Controller Asynchronous Callback
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
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
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
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") }
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") }
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") }
View Controller - UI ⾃自動化測試 func testUI() { //Arrange XCUIApplication().launch()
//Act XCUIApplication().buttons[“button"].tap() //Assert XCTAssertTrue(XCUIApplication().title == "Pressed") } • UITests Bundle • XCUIApplication
Fake, Mock, Stub • 模擬互動⾏行行為 1. 當 Class 有 2.
當受測⾏行行為需要很長的執⾏行行時間時,ex: URLSession 3. 當受測物件使⽤用外部 library 時,ex: NSUserDefault 外部相依性
• Stub (虛設常式) 在測試的過程有外部相依,模擬期望回傳值 • Mock (模擬物件) 模擬受測物件,驗證當執⾏行行某些⾏行行為時,應有預期反應 • Fake
(假物件) Stub, Mock 模擬物件的統稱 Fake, Mock, Stub 引⽤用:單元測試的藝術
測試 模擬物件 待測試類別 stub 驗證 互動 輔助模擬,使待測類 別的⾏行行為能夠被測出 引⽤用:單元測試的藝術
測試 模擬物件 待測試類別 mock 驗證 互動 模擬待測⽬目標的 ⾏行行為。 在模擬物件中, 可以記錄互動資
訊來來進⾏行行驗證。 引⽤用:單元測試的藝術
Stub Example class StubTimeMachineAPI: TimeMachineAPI { var videoUrl = "https://www.youtube.com/watch?v=SQ8aRKG9660"
func getVideoFor(year: Int) -> String { return videoUrl } }
Mock Example class MockTimeMachine: TimeMachine { var timeTravelWasCalled = false
func timeTravelTo(year: Int) { timeTravelWasCalled = true } }
Wrap Up
Unit-Testing Before Debugging After bug bug bug bug bug bug
bug bug bug vs
在開發階段, 確保程式碼的可靠性 So, let’s TDD. GOAL
RED GREEN REFACTOR 在開發的過程 有個⼈人⼀一直在幫你檢查邏輯 你會很專⼼心在當前的問題上 你會發現你沒有你想的那麼聰明
改善程式碼品質的開發流程 ⼩小步向前,才不會跌倒
We are hiring @KKBOX @UtaPass
Q & A
相關資源 • 91 哥 - 30 天快速上⼿手 TDD • 立堯
- Medium • Test-Driven iOS Development • Document - XCTest Framework • Cracking the coding interview