- Password Validator kata with starter code and tests - Shopping Cart kata with starter code and tests - FizzBuzz reference code (from live demo) - Setup guide and TDD reference card - No solutions included (participants implement themselves)
276 lines
7.8 KiB
Markdown
276 lines
7.8 KiB
Markdown
# 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<String>());
|
|
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<DivisionByZeroException>()));
|
|
|
|
// 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.**
|