【TDD】測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(建議收藏反復(fù)練習(xí))

原視頻地址鏈接:https://www.youtube.com/watch?v=B1j6k2j2eJg
代碼地址:https://github.com/ArjanCodes/2021-tdd
本文為大致翻譯以及操作實(shí)踐。
測(cè)試驅(qū)動(dòng)開(kāi)發(fā)概念
測(cè)試驅(qū)動(dòng)開(kāi)發(fā),也稱(chēng)為紅綠重構(gòu),英文全稱(chēng) Test-Driven Development,簡(jiǎn)稱(chēng) TDD,是一種不同于傳統(tǒng)軟件開(kāi)發(fā)流程的新型的開(kāi)發(fā)方法。它要求在編寫(xiě)某個(gè)功能的代碼之前先編寫(xiě)測(cè)試代碼,然后只編寫(xiě)使測(cè)試通過(guò)的功能代碼,通過(guò)測(cè)試來(lái)推動(dòng)整個(gè)開(kāi)發(fā)的進(jìn)行。
測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的五個(gè)步驟
step1 編寫(xiě)測(cè)試,編寫(xiě)功能代碼前,先編寫(xiě)在滿足功能規(guī)范時(shí)才能通過(guò)的測(cè)試開(kāi)始 step2 運(yùn)行測(cè)試,并確保它 Fail,此時(shí)意味著你的測(cè)試生效(預(yù)期就是 fail) step3 編寫(xiě)最簡(jiǎn)單的代碼,以便測(cè)試可以通過(guò),但是不必在此步驟做的完美 step4 確保所有測(cè)試可以通過(guò),包括舊的測(cè)試項(xiàng),確保新功能符合規(guī)范,并且不會(huì)對(duì)其它東西造成破壞 step5 重構(gòu)和改進(jìn)代碼
代碼實(shí)例講解
我們按照上述的五個(gè)步驟,依次講解
step1:想實(shí)現(xiàn)一個(gè)支付員工工資的功能,先不實(shí)現(xiàn)這個(gè)功能,把大體結(jié)構(gòu)寫(xiě)出來(lái):
關(guān)于 dataclass 的用法,看我文章:每日 Python 小技巧--dataclass
"""
Very advanced Employee management system.
"""
from dataclasses import dataclass
@dataclass
class Employee:
"""Basic representation of an employee."""
name: str
employee_id: int
pay_rate: float = 100.0
hours_worked: float = 0.0
employer_cost: float = 1000.0
has_commission: bool = True
commission: float = 100.0
contracts_landed: int = 0
def compute_payout(self) -> float: # 此處沒(méi)有實(shí)現(xiàn)功能
"""Compute how much the employee should be paid."""
raise NotImplementedError()
step2:編寫(xiě)測(cè)試并運(yùn)行,此時(shí)運(yùn)行應(yīng)該都是失敗的,因?yàn)楣δ軟](méi)有實(shí)現(xiàn):
"""
Employee class tests.
"""
import unittest
from employee import Employee
NAME: str = "Arjan"
EMPLOYEE_ID: int = 12345
class TestEmployeeComputePayout(unittest.TestCase):
"""Test the compute_payout method of the Employee class."""
def setUp(self):
"""Set up test fixtures."""
self.arjan = Employee(name=NAME, employee_id=EMPLOYEE_ID)
def test_employee_payout_returns_a_float(self):
"""Whether payout returns a float."""
self.assertIsInstance(self.arjan.compute_payout(), float)
def test_employee_payout_no_commission_no_hours(self):
"""Whether payout is correctly computed in case of no commission and no hours worked."""
self.assertAlmostEqual(self.arjan.compute_payout(), 1000.0)
def test_employee_payout_no_commission(self):
"""Whether payout is correctly computed in case of no commission and 10 hours worked."""
self.arjan.hours_worked = 10.0
self.assertAlmostEqual(self.arjan.compute_payout(), 2000.0)
def test_employee_payout_with_commission(self):
"""
Whether payout is correctly computed in case of
10 contracts landed and 10 hours worked.
"""
self.arjan.hours_worked = 10.0
self.arjan.contracts_landed = 10
self.assertAlmostEqual(self.arjan.compute_payout(), 3000.0)
def test_employee_payout_commission_disabled(self):
"""
Whether payout is correctly computed in case of
10 contracts landed and 10 hours worked,
but commission is disabled.
"""
self.arjan.hours_worked = 10.0
self.arjan.contracts_landed = 10
self.arjan.has_commission = False
self.assertAlmostEqual(self.arjan.compute_payout(), 2000.0)
if __name__ == "__main__":
unittest.main()
step3:實(shí)現(xiàn)最基礎(chǔ)的功能,部分 case 可以通過(guò)測(cè)試:
"""
Very advanced Employee management system.
"""
from dataclasses import dataclass
@dataclass
class Employee:
"""Basic representation of an employee."""
name: str
employee_id: int
pay_rate: float = 100.0
hours_worked: float = 0.0
employer_cost: float = 1000.0
has_commission: bool = True
commission: float = 100.0
contracts_landed: int = 0
def compute_payout(self) -> float: # pass 1 fail 4
"""Compute how much the employee should be paid."""
payout = self.pay_rate * self.hours_worked
return payout
step4:逐步完善:
def compute_payout(self) -> float: # pass 4 fail 1
"""Compute how much the employee should be paid."""
payout = self.pay_rate * self.hours_worked + self.employer_cost
return payout
def compute_payout(self) -> float: # pass 5 fail 0
"""Compute how much the employee should be paid."""
payout = self.pay_rate * self.hours_worked + self.employer_cost
if self.has_commission:
payout += self.commission * self.contracts_landed
return payout
step5:重構(gòu)代碼,此時(shí)我更改一些條件,修改相應(yīng)的計(jì)算公式,那么所有的測(cè)試 case 都可以直接復(fù)用,高效準(zhǔn)確:
注意和上面的差別:employer_cost 這個(gè)費(fèi)用,給改成了更細(xì)致的三項(xiàng),但是測(cè)試結(jié)果依然是 pass
"""
Very advanced Employee management system.
"""
from dataclasses import dataclass
@dataclass
class Employee:
"""Basic representation of an employee."""
name: str
employee_id: int
pay_rate: float = 100.0
hours_worked: float = 0.0
# employer_cost: float = 1000.0
employer_office_costs: float = 200.0
employer_401k_costs: float = 400.0
employer_suppoer_costs: float = 400.0
has_commission: bool = True
commission: float = 100.0
contracts_landed: int = 0
def compute_payout(self) -> float:
"""Compute how much the employee should be paid."""
employer_cost = self.employer_office_costs + self.employer_401k_costs + self.employer_suppoer_costs
payout = self.pay_rate * self.hours_worked + employer_cost
if self.has_commission:
payout += self.commission * self.contracts_landed
return payout
如果你有任何疑問(wèn),也可以通過(guò)公眾號(hào)里我的聯(lián)系方式加我好友,我將盡自己所能為你答疑。
猜你喜歡

評(píng)論
圖片
表情
