【TDD】测试驱动开发(建议收藏反复练习)
原视频地址链接:https://www.youtube.com/watch?v=B1j6k2j2eJg
代码地址:https://github.com/ArjanCodes/2021-tdd
本文为大致翻译以及操作实践。
测试驱动开发概念
测试驱动开发,也称为红绿重构,英文全称 Test-Driven Development,简称 TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。
测试驱动开发的五个步骤
step1 编写测试,编写功能代码前,先编写在满足功能规范时才能通过的测试开始 step2 运行测试,并确保它 Fail,此时意味着你的测试生效(预期就是 fail) step3 编写最简单的代码,以便测试可以通过,但是不必在此步骤做的完美 step4 确保所有测试可以通过,包括旧的测试项,确保新功能符合规范,并且不会对其它东西造成破坏 step5 重构和改进代码
代码实例讲解
我们按照上述的五个步骤,依次讲解
step1:想实现一个支付员工工资的功能,先不实现这个功能,把大体结构写出来:
关于 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: # 此处没有实现功能
"""Compute how much the employee should be paid."""
raise NotImplementedError()
step2:编写测试并运行,此时运行应该都是失败的,因为功能没有实现:
"""
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:实现最基础的功能,部分 case 可以通过测试:
"""
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:重构代码,此时我更改一些条件,修改相应的计算公式,那么所有的测试 case 都可以直接复用,高效准确:
注意和上面的差别:employer_cost 这个费用,给改成了更细致的三项,但是测试结果依然是 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
如果你有任何疑问,也可以通过公众号里我的联系方式加我好友,我将尽自己所能为你答疑。
猜你喜欢
评论