Mastering Test‑Driven Development: From Red to Green and Beyond
This article explains the fundamentals of Test‑Driven Development (TDD), highlights common misconceptions, guides tool selection, and walks through a complete Java TDD case study—including a strange calculator example, multi‑iteration refactoring, and DDD‑based testing—showing how to improve code quality and maintainability.
1. Introduction
The article introduces Test‑Driven Development (TDD) as a software development approach that requires writing tests before code, then writing just enough code to pass the tests, and finally refactoring while keeping all tests green.
2. TDD Basic Process
2.1 Step 1 – Write a Test (Red)
Before any implementation, developers create a failing test that describes the desired behavior.
2.2 Step 2 – Run the Test (Red)
Since no code exists yet, the test fails (often shown as a red indicator in the IDE).
2.3 Step 3 – Write Code (Green)
Implement just enough code to make the test pass, turning the indicator green.
2.4 Step 4 – Run the Test (Green)
Execute the test suite to ensure all tests pass.
2.5 Step 5 – Refactor Code
Improve the code structure without changing its behavior, then re‑run all tests to confirm they still pass.
2.6 Step 6 – Run Tests After Refactor
Verify that refactored code still satisfies all test cases.
3. Common Misconceptions
Unit testing is not the same as TDD.
Confusing integration tests with unit tests.
Skipping unit tests under tight schedules.
Writing tests after code is completed.
Insisting on 100% coverage without considering cost‑benefit.
Assuming tests only need to run once.
To check if a test is truly a unit test, disconnect the network; if the test still runs, it is a unit test.
4. TDD Tool Selection
JUnit 5 (or TestNG) for Java unit testing.
Mockito (or PowerMock) for mocking dependencies.
JaCoCo for measuring test coverage.
Allure for generating test reports.
5. TDD Case Study – "Strange Calculator"
5.1 First Iteration
Requirement: int calculate(int input) should return input‑1 for positive numbers, 0 for zero, and input+1 for negative numbers.
public class StrangeCalculator {
public int calculate(int input) {
if (input > 0) {
return input - 1;
} else if (input < 0) {
return input + 1;
} else {
return 0;
}
}
}Test class example:
public class StrangeCalculatorTest {
private StrangeCalculator calculator;
@BeforeEach
void setup() { calculator = new StrangeCalculator(); }
@Test @DisplayName("Input > 0 returns input‑1")
void givenGreaterThan0() {
Assertions.assertEquals(0, calculator.calculate(1));
}
@Test @DisplayName("Input < 0 returns input+1")
void givenLessThan0() {
Assertions.assertEquals(0, calculator.calculate(-1));
}
@Test @DisplayName("Input == 0 returns 0")
void givenEquals0() {
Assertions.assertEquals(0, calculator.calculate(0));
}
}5.2 Second Iteration
New requirement: for 0 < input < 100, return the square of the input; for input ≥ 100, keep the original "‑1" logic.
public int calculate(int input) {
if (input >= 100) {
return input - 1; // old logic
} else if (input > 0) {
return input * input; // new logic
} else if (input < 0) {
return input + 1;
} else {
return 0;
}
}Additional tests were added for the new boundaries, the old givenGreaterThan0 test was removed, and all tests passed.
5.3 Refactoring
Private helper methods were extracted for each boundary condition, improving readability and maintainability.
private int doPositiveSquare(int input) { return input * input; }
private int doPositiveMinusOne(int input) { return input - 1; }
private int doNegativePlusOne(int input) { return input + 1; }
private int doZero() { return 0; }6. DDD‑Based TDD Practice
The article extends the TDD approach to a Domain‑Driven Design (DDD) project, covering unit tests for the DAO layer (using an in‑memory H2 database), Service layer (mocking infrastructure with Mockito), Controller layer (using MockMvc), and domain entities/value objects.
6.1 DAO Layer
public interface CmsArticleMapper {
int insert(CmsArticle record);
CmsArticle selectByPrimaryKey(Long id);
// other CRUD methods
}6.2 Service Layer
@Service
public class ArticleServiceImpl implements ArticleService {
@Resource private CmsArticleMapper mapper;
@Resource private IdServiceGateway idGateway;
// createDraft, getById implementations
}6.3 Controller Layer
@RestController
@RequestMapping("/article")
public class ArticleController {
@Resource private ArticleService service;
@PostMapping("/createDraft")
public void createDraft(@RequestBody CreateDraftCmd cmd) { service.createDraft(cmd); }
@GetMapping("/get")
public CmsArticle get(Long id) { return service.getById(id); }
}7. Conclusion
The article demonstrates that adopting TDD requires an initial effort but ultimately yields more reliable, maintainable code across simple utilities, layered architectures, and DDD‑oriented systems.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
