Files
2025-11-30 09:06:38 +08:00

4.7 KiB

TDD Language-Specific Examples

This guide provides concrete TDD examples in multiple programming languages, showing the RED-GREEN-REFACTOR cycle.

RED Phase Examples

Write one minimal test showing what should happen.

Rust

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn retries_failed_operations_3_times() {
        let mut attempts = 0;
        let operation = || -> Result<&str, &str> {
            attempts += 1;
            if attempts < 3 {
                Err("fail")
            } else {
                Ok("success")
            }
        };

        let result = retry_operation(operation);

        assert_eq!(result, Ok("success"));
        assert_eq!(attempts, 3);
    }
}

Running the test:

cargo test tests::retries_failed_operations_3_times

Swift

func testRetriesFailedOperations3Times() async throws {
    var attempts = 0
    let operation = { () -> Result<String, Error> in
        attempts += 1
        if attempts < 3 {
            return .failure(RetryError.failed)
        }
        return .success("success")
    }

    let result = try await retryOperation(operation)

    XCTAssertEqual(result, "success")
    XCTAssertEqual(attempts, 3)
}

Running the test:

swift test --filter RetryTests.testRetriesFailedOperations3Times

TypeScript

describe('retryOperation', () => {
  it('retries failed operations 3 times', async () => {
    let attempts = 0;
    const operation = () => {
      attempts++;
      if (attempts < 3) {
        throw new Error('fail');
      }
      return 'success';
    };

    const result = await retryOperation(operation);

    expect(result).toBe('success');
    expect(attempts).toBe(3);
  });
});

Running the test (Jest):

npm test -- --testNamePattern="retries failed operations"

Running the test (Vitest):

npm test -- -t "retries failed operations"

Why These Are Good

  • Clear names describing the behavior
  • Test real behavior, not mocks
  • One thing per test
  • Shows desired API

Bad Example

test('retry', () => {
    let mockCalls = 0;
    const mock = () => {
        mockCalls++;
        return 'success';
    };
    retryOperation(mock);
    expect(mockCalls).toBe(1); // Tests mock, not behavior
});

Why this is bad:

  • Vague name
  • Tests mock behavior, not real retry logic

GREEN Phase Examples

Write simplest code to pass the test.

Rust

fn retry_operation<F, T, E>(mut operation: F) -> Result<T, E>
where
    F: FnMut() -> Result<T, E>,
{
    for i in 0..3 {
        match operation() {
            Ok(result) => return Ok(result),
            Err(e) => {
                if i == 2 {
                    return Err(e);
                }
            }
        }
    }
    unreachable!()
}

Swift

func retryOperation<T>(_ operation: () async throws -> T) async throws -> T {
    var lastError: Error?
    for attempt in 0..<3 {
        do {
            return try await operation()
        } catch {
            lastError = error
            if attempt == 2 {
                throw error
            }
        }
    }
    throw lastError!
}

TypeScript

async function retryOperation<T>(
  operation: () => Promise<T>
): Promise<T> {
  let lastError: Error | undefined;
  for (let i = 0; i < 3; i++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error as Error;
      if (i === 2) {
        throw error;
      }
    }
  }
  throw lastError;
}

Bad Example - Over-engineered (YAGNI)

async function retryOperation<T>(
  operation: () => Promise<T>,
  options: {
    maxRetries?: number;
    backoff?: 'linear' | 'exponential';
    onRetry?: (attempt: number) => void;
    shouldRetry?: (error: Error) => boolean;
  } = {}
): Promise<T> {
  // Don't add features the test doesn't require!
}

Why this is bad: Test only requires 3 retries. Don't add:

  • Configurable retries
  • Backoff strategies
  • Callbacks
  • Error filtering

...until a test requires them.

Test Requirements

Every test should:

  • Test one behavior
  • Have a clear name
  • Use real code (no mocks unless unavoidable)

Verification Commands by Language

Rust

# Single test
cargo test tests::test_name

# All tests
cargo test

# With output
cargo test -- --nocapture

Swift

# Single test
swift test --filter TestClass.testName

# All tests
swift test

# With output
swift test --verbose

TypeScript (Jest)

# Single test
npm test -- --testNamePattern="test name"

# All tests
npm test

# With coverage
npm test -- --coverage

TypeScript (Vitest)

# Single test
npm test -- -t "test name"

# All tests
npm test

# With coverage
npm test -- --coverage