[TDD] Test Driven Development

Keywords: Python

Welcome to the WeChat public number "Python Kitchen, learn new Python knowledge with me every day", and add blogger Vx:yf03064131 for easy communication and learning.

Or station B searches for a new one

Original video address link: click here
Code address: click here
This is a general translation and operational practice.

Test-Driven Development Concepts

Test-driven development, also known as red-green refactoring, or Test-Driven Development, or TDD, is a new development method that is different from the traditional software development process. It requires that you write test code before you write code for a function, then write only the function code that makes the test pass, and then use the test to drive the whole development process.

Five steps for test-driven development

  • step1
    Write tests, and before you write functional code, start by writing tests that pass only when the functional specifications are met
  • step2
    Run the test and make sure it fails, which means your test is valid (expected to be fail)
  • step3
    Write the simplest code so that tests can pass, but don't have to be perfect at this step
  • step4
    Ensure that all tests pass, including older ones, that the new functionality meets the specifications, and that nothing else is damaged
  • step5
    Refactoring and improving code

Code Instance Explanation

Let's follow these five steps and explain them one by one

step1: To achieve a function of paying employees'wages, first do not implement this function, write out the general structure:

For the usage of dataclass, see my article: Daily Python Tips - 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: Write tests and run them, all of which should fail because the functionality is not implemented:

"""
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: to achieve the most basic functions, some case s can pass the test:

"""
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: gradually improve:

    def compute_payout(self) -> float: # pass 4 fail 1
        """Compute how much the employee should be paid."""
        
        payout = self.pay_rate * self.hours_worked 
        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 
        return payout

step5: refactoring the code, at this time I change some conditions, modify the corresponding calculation formula, then all test case s can be directly reused, efficient and accurate:

Note the difference: the employer_cost cost has been changed to three more detailed items, but the test result is still 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

Posted by youngsei on Tue, 14 Sep 2021 17:58:48 -0700