# TDD Reference Card **Quick reference for Test-Driven Development practice** --- ## The RED-GREEN-REFACTOR Cycle ``` ┌─────────────────────────────────────────────────┐ │ │ │ 1. RED Write a failing test │ │ ↓ (Proves the test can fail) │ │ │ │ 2. GREEN Write minimal code to pass │ │ ↓ (Make it work, don't make it perfect)│ │ │ │ 3. REFACTOR Clean up code │ │ ↓ (Remove duplication, improve names)│ │ │ │ 4. REPEAT Next failing test │ │ ↑ │ │ └───────────────────────────────────────────┘ ``` --- ## The Discipline ### RED - Write a Failing Test - **Write the test first**, before any production code - Run it and **watch it fail** (RED ❌) - Make sure it fails **for the right reason** - If it passes immediately, the test isn't testing anything new ### GREEN - Make It Pass - Write the **simplest code** that makes the test pass - Don't worry about perfection yet - **Hardcoding is okay** if it passes the test - Run the test and see it **pass** (GREEN ✅) - If you're tempted to write "just one more line"—STOP and run the test ### REFACTOR - Clean Up - Now that tests are passing, **improve the code** - Remove duplication - Clarify names - Extract methods or classes - **Run tests after every change** to ensure they stay green - If tests go RED during refactoring, undo and try smaller steps --- ## When to Refactor Look for these **code smells**: | Smell | Refactoring | |-------|-------------| | **Duplication** | Extract method, introduce constant | | **Unclear names** | Rename variable/method/class | | **Long method** | Extract smaller methods | | **Nested conditionals** | Extract methods, introduce polymorphism | | **Magic numbers** | Replace with named constants | | **God class** | Split responsibilities into multiple classes | **Rule of thumb:** If you have to write a comment explaining what code does, the code needs better names. --- ## Common Test Patterns ### Arrange-Act-Assert (AAA) ```dart test('calculates total price correctly', () { // ARRANGE - Set up test data final cart = ShoppingCart(); cart.addItem(CartItem(name: 'Apple', price: 1.50, quantity: 3)); // ACT - Perform the action being tested final total = cart.subtotal(); // ASSERT - Verify the result expect(total, equals(4.50)); }); ``` ### setUp() for Common Setup ```dart void main() { late Calculator calculator; setUp(() { calculator = Calculator(); // Runs before EACH test }); test('adds two numbers', () { expect(calculator.add(2, 3), equals(5)); }); } ``` ### Test Naming Use descriptive names that explain **what** is being tested: ✅ **Good**: `test('rejects password shorter than 8 characters', ...)` ❌ **Bad**: `test('test1', ...)` or `test('validation works', ...)` --- ## Dart Test Assertions - Quick Reference ```dart // Equality expect(actual, equals(expected)); expect(result, isTrue); expect(result, isFalse); // Comparison expect(value, greaterThan(10)); expect(value, lessThan(5)); expect(value, closeTo(0.30, 0.001)); // For floating point // Type checks expect(object, isA()); expect(value, isNull); expect(value, isNotNull); // Collections expect(list, isEmpty); expect(list, isNotEmpty); expect(list, contains('item')); expect(list, containsAll(['a', 'b'])); expect(list, hasLength(3)); // Exceptions expect(() => validatePassword(''), throwsArgumentError); expect(() => divide(1, 0), throwsA(isA())); // Strings expect(text, startsWith('Hello')); expect(text, endsWith('world')); expect(text, matches(RegExp(r'\d+'))); ``` --- ## Quick Tips for TDD Success ### 1. Start Small Begin with the **simplest possible test**: - Empty input → sensible output - Single item → correct behavior - Then add complexity ### 2. One Test at a Time - Uncomment or write **one test** - Make it pass - Then move to the next - **Don't skip ahead!** ### 3. Baby Steps Each cycle should take **2-5 minutes**, not 20 minutes: - If a test takes too long, it's testing too much - Break it into smaller tests ### 4. Run Tests Frequently After **every code change**: - Immediately see if something broke - Faster feedback = faster learning ### 5. Trust the Process The design **emerges** from the tests: - You don't need to design everything upfront - Simple tests lead to general solutions - Complex scenarios often work without explicit implementation ### 6. Refactor Fearlessly With passing tests, you can: - Rename aggressively - Restructure boldly - Optimize confidently - If tests go RED, just undo --- ## Common Mistakes to Avoid | Mistake | Why It's Bad | Fix | |---------|--------------|-----| | Writing code before test | No way to know if test is valid | Always RED first | | Writing multiple tests at once | Overwhelming, loses focus | One test at a time | | Skipping REFACTOR | Technical debt accumulates | Clean up after each GREEN | | Not running tests after refactor | Might break something unknowingly | Run tests constantly | | Testing implementation details | Tests become brittle | Test behavior, not internals | | Making tests pass by hardcoding | Doesn't prove general solution | Add another test to force generalization | --- ## What to TDD vs. What to Skip ### ✅ Great for TDD: - **Business logic**: Calculations, validations, algorithms - **Domain models**: Value objects, entities with rules - **Utilities**: String parsers, formatters, converters - **APIs**: Request/response handling, error cases ### ⚠️ Less Useful for TDD: - **UI layouts**: Visual appearance is hard to test - **Framework glue**: Wiring dependencies, configuration - **Database queries**: Better tested with integration tests - **Third-party integrations**: Better mocked or integration tested **Rule:** If behavior matters and can be verified programmatically, use TDD. --- ## When You Get Stuck ### "I don't know what test to write next" Ask: **"What's the simplest behavior this doesn't handle yet?"** - Start with zero/empty cases - Then one item - Then two items - Then edge cases ### "The test is too complex" **Break it down:** - Can you test just one part of the behavior? - Can you introduce a helper method to simplify setup? ### "I can't make it pass without writing a lot of code" **That's a sign:** - The test might be too big (break into smaller tests) - OR you're missing an intermediate test - OR you need to refactor existing code first ### "Tests are passing but the design feels wrong" **Good news:** - You have tests as a safety net! - **Refactor now** while tests are green - Improve names, extract methods, restructure --- ## Resources for More Practice ### After This Workshop: - **tdd-katas repo**: github.com/dhemasnurjaya/tdd-katas - 5 complete katas with commit history - Roman Numerals, Bowling Game, Gilded Rose, String Calculator, Mars Rover ### Books: - *Test-Driven Development* by Kent Beck - *Clean Code* by Robert C. Martin - *Growing Object-Oriented Software, Guided by Tests* by Freeman & Pryce ### Online Katas: - Kata-Log (kata-log.rocks) - Coding Dojo (codingdojo.org) - Exercism (exercism.org) --- ## Remember > "Clean code that works." — Ron Jeffries TDD is a **skill**. Like any skill: - It feels awkward at first - Practice makes it automatic - The rhythm becomes second nature **Keep practicing. Keep the cycle tight. Let the tests guide you.**