iT邦幫忙

0

iOS APP iOS Test-Driven Development by Tutorials free section 學習筆記-the TDD Cycle

iOS APP iOS Test-Driven Development by Tutorials free section 學習筆記-the TDD Cycle

tags: TDD day

TDD Cycle

上一篇筆記,您了解了測試驅動的開發可以歸結為一個簡單的過程,稱為TDD Cycle。它有四個步驟,通常被“color coded”,如下所示:
我們稱它為“Red-Green-Refactor Cycle”

圖片來源
顏色是什麼意思?

  • 失敗時,以 Red為顏色標記
  • 通過時,以 green 為顏色標記
  • 通過後進入 Refactor階段

不斷的循環。

注意

我的筆記會直接跳過playground的章節,建議看完playground的章節再看我的筆記:
the TDD Cycle

Getting started

clone我的專案,嘗試我建制專案的過程吧。
clone Alvin的專案

需求

我希望Login in 的 button ,在帳號與密碼不符合規範時,是不能按的。

graph LR;
 button.state是disable-->|達成條件|button.state是normal
graph LR;
 button.state是normal-->|未達成條件|button.state是disable

Red : Write a failing test

為了讓程式碼有好的可讀性,我們在測試的命名要注意

Test nomenclaturn

試著遵循一些TDD命名法則。我們來看看範例:

func testAppModel_whenStarted_isInInProgressState() {}

test funtion 的名稱應該要描述test。test名稱顯示在test logs中。當發生錯誤時,必續邀可以立即了解問題所在。所以要避免test1,test2這種命名。
這裡使用的命名方案最多包括四個部分:

  1. 所有測試必須以開頭test。
  2. AppModel這表示一個AppModel正在測試的系統(sut)。
  3. whenStarted 是條件或狀態變化,是條件的觸發者。
  4. isInInProgressState當when發生後狀態應該受到的變化。

Try it

我要測試的條件為:
當LoginPageViewController在帳號與密碼都已經服合條件時,loginButton的state要是Normal的

func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){}

接著在裡面放入三個註解

    func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){
        //given
        // when
        //then
    }

這是為了方便理解建制這個 Test的流程。

  • Given 在特定的條件下
  • When 當某個行為發生時
  • Then 預期要發生的結果

Given 帳號與密碼都符合條件下

    func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){
        //given
        let state:LoginState = .bothCorrect
        // when
        //then
    }

When 當LoginPageViewController為這個狀態時

    func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){
        //given
        let state:LoginState = .bothCorrect
        // when
        sut.loginState = state
        //then
    }

Then loginButton的state應該要是normal。

    func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){
        //given
        let state:LoginState = .bothCorrect
        // when
        sut.loginState = state
        //then
        let loginState = sut.loginPageView.loginButton.state
        XCTAssertEqual(loginState, UIButton.State.normal)
    }

開始測試

按下 command + U 開始測試。

結果測試錯誤,別擔心。這是一定會遇到的流程。我們來嘗試通過測試。

Green : Make the test pass

1. 寫一個Loginable的 protocol

protocol Loginable {
    func changeLoginButtonState(loginState:LoginState)
}

2. 讓 LoginPageViewController conform Loginable 這個 protocol 然後delegate也設置完成。

import UIKit
protocol Loginable {
    func changeLoginButtonState(loginState:LoginState)
}
class  LoginPageViewController: UIViewController{
    let loginPageView = LoginPageView()
    public var loginState:LoginState = .bothError
    //MARK: - loadView()
    override func loadView() {
        self.view = loginPageView
        loginPageView.delegate = self
    }
    //MARK: - viewDidLoad()
    override func viewDidLoad() {
        super .viewDidLoad()
    }
}
    //MARK: Loginable
extension LoginPageViewController:Loginable{
    func changeLoginButtonState(loginState: LoginState) {
        if loginState == .bothCorrect {
            loginPageView.loginButton.isEnabled = true
        }
    }
}

但是僅僅這樣是不夠的,我還要能夠監測UITextField有沒有輸入文字,加入UITextField輸入的delegate。

import UIKit
protocol Loginable {
    func changeLoginButtonState(loginState:LoginState)
}
class  LoginPageViewController: UIViewController{
    let loginPageView = LoginPageView()
    public var loginState:LoginState = .bothError{
        didSet{
            changeLoginButtonState(loginState: self.loginState)
        }
    }
    
    //MARK: - loadView()
    override func loadView() {
        self.view = loginPageView
        loginPageView.loginableDelegate = self
        loginPageView.usernameTextField.delegate = self
        loginPageView.passwordTestField.delegate = self
    }
    //MARK: - viewDidLoad()
    override func viewDidLoad() {
        super .viewDidLoad()
    }
}
    //MARK: Loginable
extension LoginPageViewController:Loginable{
    func changeLoginButtonState(loginState: LoginState) {
        if loginState == .bothCorrect {
            loginPageView.loginButton.isEnabled = true
        }
    }
}
extension LoginPageViewController:UITextFieldDelegate{
    func textFieldDidEndEditing(_ textField: UITextField) {
        if textField.text!.count > 10{
            loginState = .bothCorrect
            changeLoginButtonState(loginState: loginState)
        }
    }
}

接著 command + U 再測試一次

很好,測試成功了。

3. 接下來,進入 Refactor 的流程

我重構了LoginPageViewController,將判斷帳號與密碼是否輸入的邏輯切出去。

import UIKit
protocol Loginable {
    func changeLoginButtonState(loginState:LoginState)
}
class  LoginPageViewController: UIViewController{
    let loginPageView = LoginPageView()
    public var loginState:LoginState = .bothError{
        didSet{
            changeLoginButtonState(loginState: self.loginState)
        }
    }
    
    //MARK: - loadView()
    override func loadView() {
        self.view = loginPageView
        loginPageView.loginableDelegate = self
        loginPageView.usernameTextField.delegate = self
        loginPageView.passwordTestField.delegate = self
    }
    //MARK: - viewDidLoad()
    override func viewDidLoad() {
        super .viewDidLoad()
    }
}
    //MARK: Loginable
extension LoginPageViewController:Loginable{
    func changeLoginButtonState(loginState: LoginState) {
        if loginState == .bothCorrect {
            loginPageView.loginButton.isEnabled = true
        }
    }
}
    //MARK: UITextFieldDelegate
extension LoginPageViewController:UITextFieldDelegate{
    func textFieldDidEndEditing(_ textField: UITextField) {
        let tag = textField.tag
        if textField.text!.count > 10{
           loginState =  loginState.updateState(tag: tag)
        }
    }
}

  1. 再一次的 unit test

    如果驗證是成功的,代表重構是成功的。

開始

我們完成了一次 TDD Cycle,別忘了 TDD 是迭代開發軟體的方法,所以這只是第一步,這或許是很漫長的,但是每個扎實的步伐,將會帶領你的程式碼步向於完美。
下一個 TDD Cycle


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言