docs: add TDD conventions and testing guide to CLAUDE.md

Covers testing file conventions, Red→Green→Refactor cycle with
example prompts, common anti-patterns, test runner commands, and
the auto-run hook setup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 22:09:44 +02:00
parent fa035781fd
commit 381b8f4489
+100
View File
@@ -16,6 +16,106 @@ curl "http://localhost:8080/graph.svg?theme=dark" -o graph.svg
curl "http://localhost:8080/health"
```
### Testing conventions (apply to every test file)
- `declare(strict_types=1)` at the top of every test file
- Test classes are `final` and extend `TestCase`
- Methods: `#[Test]` attribute + `it_` prefix + `snake_case` — e.g. `it_returns_empty_when_no_contributions`
- Structure: Arrange → Act → Assert, separated by blank lines; one assertion per test when possible
- Use `$this->assert*()` not `self::assert*()`
```php
<?php
declare(strict_types=1);
namespace App\Tests\Service;
use App\Service\SvgRenderer;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
final class SvgRendererTest extends TestCase
{
#[Test]
public function it_renders_an_svg_with_no_contributions(): void
{
$renderer = new SvgRenderer();
$svg = $renderer->render([], 'light');
$this->assertStringContainsString('<svg', $svg);
}
}
```
### TDD: Red → Green → Refactor
Always drive new features and bug fixes with this cycle. Claude writes implementation first by default — override that with explicit prompts.
**Phase 1 — Red (write a failing test first)**
```
Write a failing test for [feature description].
Do NOT write the implementation yet.
The test should fail because the class/method does not exist.
```
**Phase 2 — Green (minimal implementation)**
```
The tests are written and failing. Now implement the minimum code to make them pass. Nothing more.
```
**Phase 3 — Refactor**
```
Tests are green. Refactor the implementation for [readability / removing duplication / naming].
Run php bin/phpunit after each change to confirm tests stay green.
```
**Common anti-patterns**
| Wrong prompt | Why it breaks TDD | Correct prompt |
|---|---|---|
| "Write tests for this feature" | Claude implements first, then fits tests to it | "Write **failing** tests for [feature]. Stop before any implementation." |
| "Add tests and implementation" | Loses the design feedback of failing tests | Two separate prompts: Red, then Green |
| "Make the tests pass" | Encourages skipping to a green state | "Implement the minimum to make the failing tests pass." |
| Combining Red + Green in one request | No failing baseline | Always separate the two phases |
### Running tests
```bash
# Run full suite
php bin/phpunit
# Run with human-readable output
php bin/phpunit --testdox
# Run a single test file
php bin/phpunit tests/Unit/Service/SvgRendererTest.php
# Run tests matching a filter
php bin/phpunit --filter it_renders
```
### Auto-run hook
Add to `.claude/settings.json` to run PHPUnit automatically after every file edit:
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "php bin/phpunit 2>&1 | tail -20"
}
]
}
}
```
## Docker
```bash