TDD 有助於理清思路。實踐 TDD 的步驟如下:
- 明確功能
- 驗證功能(寫測試)
- 實現功能
- 運行測試
- 修改功能 (非必須)
其中第一步是理清思路,在紙上寫寫畫畫,細分功能,不需要寫代碼。大多程序員不管三七二十一,想到什麼就寫什麼,儘快上手寫代碼,出現問題了再修補,思路不清晰。其實這一步纔是最重要的。
拿修改密碼功能舉例,例子中使用後端語言爲 Ruby,Web 框架是 RoR,測試框架 Rspec。不瞭解該語言可以把代碼當僞代碼看,畢竟 Ruby 很接近自然語言 :)
明確功能:
該功能的效果:用戶修改密碼後,密碼會變(廢話),需要使用新密碼登錄。
實現方法如下:
驗證舊密碼與現在的密碼是否一致 => 修改密碼,該功能比較簡單。驗證功能(寫測試):
創建用戶 => 調用修改密碼接口 => 檢驗密碼是否改爲新密碼
# spec/controllers/api/users_controller_spec.rb
RSpec.describe Api::UsersController type: :controller do
describe '#change_password' do
old_pwd = 'adfslkj'
new_pwd = '87234sd'
user = create(:user, password: old_pwd)
session[:user_id] = user.id # 通過會話標記登錄狀態
post(:change_password, params: { old_pwd: old_pwd, new_pwd: new_pwd, confirmed_pwd: new_pwd } )
expect(response.status).to eq(200) # 檢驗是否返回成功的響應
user.reload # 成功調用接口後,密碼應該變了,重新從數據庫獲取用戶
expect(user.valid_password?(old_pwd)).not_to be_truthy # 舊密碼失效
expect(user.valid_password?(new_pwd)).to be_truthy # 判斷傳到接口的新密碼現在是否爲有效的密碼
end
end
現在運行測試,還沒寫功能,測試肯定不通過。
$ rspec spec/controllers/api/users_controller_spec.rb
# 失敗
- 實現功能:
實現方法主要如步驟 1 所說。
驗證舊密碼與現在的密碼是否一致 => 修改密碼
# app/controllers/api/users_controller.rb
...
def change_password
old_pwd, new_pwd, confirmed_pwd = require_params!(:old_pwd, :new_pwd, :confirm_pwd)
if !current_user.valid_password?(old_pwd)
return fail_res(400, '無效的舊密碼')
end
current_user.update!(password: new_pwd) # 修改密碼
res(200, '修改成功')
end
...
看吧,很簡單,幾行代碼就搞定。不過有些異常情況沒處理,我們稍後再講。
- 運行測試、修改功能:
接下來運行測試
$ rspec spec/controllers/api/users_controller_spec.rb
# 通過.
測試通過啦。然後我們還要改進一下代碼,確認新密碼和確認密碼是否一致。
...
def change_password
old_pwd, new_pwd, confirmed_pwd = require_params!(:old_pwd, :new_pwd, :confirm_pwd)
if !current_user.valid_password?(old_pwd)
return fail_res(400, '無效的舊密碼')
end
if new_pwd != confirmed_pwd
return fail_res(400, '新密碼與確認密碼不一致')
end
current_user.update!(password: new_pwd) # 修改密碼
res(200, '修改成功')
end
...
寫好測試後,以後就可以放心修改這個接口,或者是重構跟用戶密碼有關的代碼。只要重構後可以通過測試,就比較可靠了。
要是遇到沒想到的異常情況,再補多一個測試就好了。