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”
图片来源
颜色是什么意思?
不断的循环。
注意
我的笔记会直接跳过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这种命名。
这里使用的命名方案最多包括四个部分:
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 UIKitprotocol 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: Loginableextension LoginPageViewController:Loginable{ func changeLoginButtonState(loginState: LoginState) { if loginState == .bothCorrect { loginPageView.loginButton.isEnabled = true } }}
但是仅仅这样是不够的,我还要能够监测UITextField有没有输入文字,加入UITextField输入的delegate。
import UIKitprotocol 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: Loginableextension 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 UIKitprotocol 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: Loginableextension LoginPageViewController:Loginable{ func changeLoginButtonState(loginState: LoginState) { if loginState == .bothCorrect { loginPageView.loginButton.isEnabled = true } }} //MARK: UITextFieldDelegateextension LoginPageViewController:UITextFieldDelegate{ func textFieldDidEndEditing(_ textField: UITextField) { let tag = textField.tag if textField.text!.count > 10{ loginState = loginState.updateState(tag: tag) } }}
再一次的 unit test
如果验证是成功的,代表重构是成功的。
开始
我们完成了一次 TDD Cycle,别忘了 TDD 是迭代开发软体的方法,所以这只是第一步,这或许是很漫长的,但是每个扎实的步伐,将会带领你的程式码步向于完美。
下一个 TDD Cycle