TDD day
在上一篇筆記,您了解了測試驅動的開發可以歸結為一個簡單的過程,稱為TDD Cycle。它有四個步驟,通常被“color coded”,如下所示:
我們稱它為“Red-Green-Refactor Cycle”
圖片來源
顏色是什麼意思?
不斷的循環。
我的筆記會直接跳過playground的章節,建議看完playground的章節再看我的筆記:
the TDD Cycle
clone我的專案,嘗試我建制專案的過程吧。
clone Alvin的專案
我希望Login in 的 button ,在帳號與密碼不符合規範時,是不能按的。
graph LR;
button.state是disable-->|達成條件|button.state是normal
graph LR;
button.state是normal-->|未達成條件|button.state是disable
為了讓程式碼有好的可讀性,我們在測試的命名要注意
試著遵循一些TDD命名法則。我們來看看範例:
func testAppModel_whenStarted_isInInProgressState() {}
test funtion 的名稱應該要描述test。test名稱顯示在test logs中。當發生錯誤時,必續邀可以立即了解問題所在。所以要避免test1,test2這種命名。
這裡使用的命名方案最多包括四個部分:
我要測試的條件為:
當LoginPageViewController在帳號與密碼都已經服合條件時,loginButton的state要是Normal的
func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){}
接著在裡面放入三個註解
func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){
//given
// when
//then
}
這是為了方便理解建制這個 Test的流程。
func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){
//given
let state:LoginState = .bothCorrect
// when
//then
}
func testLoginPageViewController_loginStateIsBothCorrect_loginButtonIsNormal(){
//given
let state:LoginState = .bothCorrect
// when
sut.loginState = state
//then
}
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 開始測試。
結果測試錯誤,別擔心。這是一定會遇到的流程。我們來嘗試通過測試。
protocol Loginable {
func changeLoginButtonState(loginState:LoginState)
}
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 再測試一次
很好,測試成功了。
我重構了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)
}
}
}
我們完成了一次 TDD Cycle,別忘了 TDD 是迭代開發軟體的方法,所以這只是第一步,這或許是很漫長的,但是每個扎實的步伐,將會帶領你的程式碼步向於完美。
下一個 TDD Cycle