TDD in Practice

How test-driven development is applied in Vivolar: red-green-refactor with real examples from the codebase.

tdd testing process

Why TDD

TDD isn’t just about testing — it’s a design tool. Writing the test first forces you to think about the API from the consumer’s perspective before writing any implementation. This leads to cleaner interfaces, smaller methods, and better separation of concerns.

In Vivolar, TDD is a non-negotiable part of the workflow. Every feature, every bug fix, every refactoring session starts with a test.

The Loop

The implementation loop is codified in the project’s CLAUDE.md:

  1. Read & Study Patterns — before writing any code, read the task spec AND at least one existing example of the same kind
  2. Implement — red → green → refactor
  3. Test — run ./mvnw verify and npm run build && npm test
  4. Fix — if anything fails, analyze and fix. Repeat until green
  5. Doc — update documentation if behavior changed
  6. Commit — stage all files, conventional commit format

Red Phase

The red phase is about defining the expected behavior:

@Test
void shouldReturnItemsFilteredByHousehold() {
    // Given items in two different households
    // When requesting items for household A
    // Then only household A's items are returned
}

The test should fail for the right reason — not because of a compile error, but because the expected behavior isn’t implemented yet. If the test passes immediately, either the feature already exists or the test is wrong.

Green Phase

Write the minimum code to make the test pass. This means:

If the minimum implementation feels too simple, that’s a signal to write more tests, not to write more code.

Refactor Phase

With green tests as a safety net, clean up:

The key rule: tests must stay green throughout refactoring. If a refactoring breaks a test, it wasn’t a refactoring — it was a behavior change.

Practical Tips from the Trenches

Start with the happy path. Get the basic scenario working before handling edge cases. Each edge case gets its own test.

Name tests like sentences. shouldReturnItemsFilteredByHousehold is better than testGetItems. The test name is documentation.

One assertion per test (usually). Multiple assertions in one test make it hard to pinpoint what broke. Exception: when the assertions are verifying different aspects of the same logical result.

Use TestContainers for integration tests. Don’t mock the database. A test that passes against mocks but fails against real PostgreSQL is worse than no test.