TDD 的核心理念是 "先写测试,再写功能",其过程遵循一个严格的循环,即 Red-Green-Refactor:
TDD 的流程
1. Red(编写失败的测试)
- 根据需求,先编写一个测试用例,描述期望的行为。
- 运行测试用例,这时测试会失败(因为功能还没实现)。
- 失败的测试是 TDD 的起点,用来验证当前功能还未完成。
2. Green(实现功能使测试通过)
- 编写代码实现功能,只编写足够通过测试的代码。
- 不追求完善或优化,只需让测试通过。
- 运行测试确保它变为绿色(成功)。
3. Refactor(重构代码)
- 在测试通过的前提下,对代码进行重构。
- 目标是提高代码质量(如消除冗余、优化性能),同时保持所有测试用例通过。
- 运行测试用例,确保重构后功能不变。
为什么先写测试?
1. 提高代码质量
- 在功能实现前明确验证条件,降低缺陷风险。
- 保证功能模块始终可测试。
2. 支持重构
- 测试用例成为保障,让开发者在重构时无需担心功能被破坏。
3. 明确需求
- 编写测试用例时,开发者必须准确理解功能需求,并转化为可验证的行为。测试引导功能开发,避免开发过程中偏离需求。
举例
接口文档:计算税收
接口概述
本接口用于根据用户提供的收入(income)和月份(months)计算应缴纳的税收。用户通过发送GET请求到指定URL,传入收入金额和月份数,接口将返回计算后的税收金额。
请求 URL
GET http://localhost:8087/getTax
请求参数
参数名 | 类型 | 必填 | 描述 |
---|---|---|---|
income | int | 是 | 用户的收入金额 |
months | int | 是 | 收入的月份数 |
请求示例
GET http://localhost:8087/getTax?income=1234&months=3
响应格式
- 格式: JSON
- 示例:
json
{
"tax": 123.45
}
响应参数
参数名 | 类型 | 描述 |
---|---|---|
tax | double | 计算出的税收金额 |
TDD
先创建接口:
java
@RestController
public class TaxController {
@Autowired
ProcessTaxService processTaxService;
@GetMapping("/getTax")
public ResponseEntity calculateTax(@RequestParam int income , @RequestParam int months) {
return ResponseEntity.ok().body(null);
}
}
我们需要在这里实现业务逻辑,将收入和月份作为用户输入并处理税收。现在我们需要实现业务逻辑。我们从单元测试开始实现它。
java
public class ProcessTaxTest {
@InjectMocks
ProcessTaxService processTaxService;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testTaxCalcualtion(){
int income = 10;
int months = 5;
double totalTax = processTaxService.calculate(income, months);
Assert.assertEquals(15.0, totalTax, 0.001);
}
}
在这里,我创建了ProcessTaxService类,它将用于实现税收计算代码。但是这个类在我们的代码库中不存在(红色标志),因此我们需要现在创建它。
java
@Service
public class ProcessTaxService {
private double taxPercentage = 0.3;
public double calculate(int income, int months) {
return income * months * taxPercentage;
}
}
重构代码,请确保传入的income
和months
参数为正整数。
java
@Service
public class ProcessTaxService {
private double taxPercentage = 0.3;
public double calculate(int income, int months) {
if (income <= 0 || months <= 0) {
throw new IllegalArgumentException("Income and months must be positive values.");
}
return income * months * taxPercentage;
}
}
java
@RestController
public class TaxController {
@Autowired
ProcessTaxService processTaxService;
@GetMapping("/getTax")
public ResponseEntity calculateTax(@RequestParam int income , @RequestParam int months) {
try {
double tax = processTaxService.calculate(income, months);
return ResponseEntity.ok().body(tax);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(null);
}
}
}
现在我们已经成功地编写了使用测试驱动方法计算总税收所需的代码。将ProcessTaxService类集成到Controller类中,在用户发送请求时计算税收。
java
@RestController
public class TaxController {
@Autowired
ProcessTaxService processTaxService;
@GetMapping("/getTax")
public ResponseEntity calculateTax(@RequestParam int income , @RequestParam int months) {
return ResponseEntity.ok().body(processTax.calculate(income, months));
}
}