Claude
Skills
Sign in
Back

phpunit-unit-test-generation

Included with Lifetime
$97 forever

Internal sub-skill. Do not auto-activate. Use only when explicitly invoked by name by another skill or agent.

AI Agents

What this skill does


# PHPUnit Test Generation

Generate Shopware-compliant PHPUnit unit tests that pass PHPStan and PHPUnit validation.

## File Write Restrictions

Write ONLY to:
- `tests/unit/**` - Unit test files

NEVER write to:
- `src/**` - Source code (read-only)
- `tests/integration/**` - Out of scope
- Any other directory

## Quick Start

1. Read the target source class
2. **Check if test is required** (see Phase 1)
3. Determine test category (A-E)
4. Apply the matching template
5. Validate with PHPStan and PHPUnit
6. Fix any errors and repeat
7. Generate completion report

---

## Phase 1: Analyze Source Class

### Step 1: Check Coverage Exclusions

Before analyzing the source class, check if the project's `phpunit.xml.dist` (or `phpunit.xml`) excludes it from coverage. Files excluded from coverage do not need unit tests.

1. **Read** `phpunit.xml.dist` from the project root
2. **Find** `<exclude>` rules inside the `<coverage>` or `<source>` section
3. **Match** the source file path against each rule:
   - `<directory suffix="X">path</directory>` — excluded if file is under `path` AND filename ends with `X`
   - `<file>path/to/File.php</file>` — excluded if relative path matches exactly
4. **If excluded** → Return SKIPPED with `skip_type: coverage_excluded` and reason: "Source file excluded from coverage by phpunit.xml.dist (`<matched-rule>`)"

If `phpunit.xml.dist` is not found, skip this step.

### Step 2: Determine If Test Is Required

Before generating any test, evaluate if the class/method requires one.

**Quick check**: Does the method body contain ONLY `return <literal|constant|property|passthrough-new|delegation>`?
- **Yes** -> NO TEST NEEDED — Return SKIPPED with `skip_type: no_logic` and reason describing the pattern (e.g., "Pure accessor - no logic to test")
- **No** (has conditionals/loops/transformations) -> Continue to Step 3

For detailed rules on what to test vs skip, see references/test-requirement-rules.md.

### Step 3: Analyze Source Structure

Read the target class to determine:
1. **Public methods** - What behaviors to test
2. **Constructor dependencies** - What to mock/stub
3. **Return types** - Expected outcomes
4. **Exception scenarios** - Error paths (see references/exception-patterns.md)
5. **Deprecation markers** - `@deprecated` tags, `Feature::triggerDeprecationOrThrow()`, `Feature::silent()`, `Feature::callSilentIfInactive()` (see references/deprecation-guards.md)

### Step 4: Detect Category

Use the decision tree to select the appropriate category:

```
Has constructor dependencies?
├── No → Is it an Exception class?
│   ├── Yes → Category E
│   └── No → Category A (DTO)
└── Yes → Uses EntityRepository?
    ├── Yes → Category D (DAL)
    └── No → Implements EventSubscriberInterface or FlowAction?
        ├── Yes → Category C (Flow/Event)
        └── No → Category B (Service)
```

For detailed category criteria, see references/category-detection.md.

---

## Phase 2: Essential Rules

Apply these mandatory conventions when generating tests.

### Quick Reference

| Rule | Requirement |
|------|-------------|
| File location | `tests/unit/` mirroring `src/` path |
| Class attribute | `#[CoversClass(TargetClass::class)]` required |
| Assertions | Use `static::` not `$this->` |
| Base class | Extend `PHPUnit\Framework\TestCase` |
| Method naming | `test` + `Action` + `Condition` + `ExpectedResult` |
| Attribute order | PHPDoc -> DataProvider -> TestDox -> method |
| One behavior | NO conditionals in tests |

### TestDox Phrasing

TestDox MUST be a **predicate phrase** starting with an action verb:
- **Good**: "creates product", "returns null", "throws exception"
- **Bad**: "It creates...", "Should return...", "Tests that..."

### Mocking Priority

1. **Real implementation** - Use actual objects when simple
2. **Shopware stubs** - `StaticEntityRepository`, `StaticSystemConfigService`, `Generator`
3. **PHPUnit mocks** - Only for external/IO dependencies

For createStub vs createMock selection, see references/mocking-patterns.md.

For complete rules, see references/essential-rules.md.

---

## Phase 3: Generate Test

### Step 1: Select Template

Based on category from Phase 1:

| Category | Template |
|----------|----------|
| A (DTO) | templates/category-a-dto.md |
| B (Service) | templates/category-b-service.md |
| C (Flow/Event) | templates/category-c-flow.md |
| D (DAL) | templates/category-d-dal.md |
| E (Exception) | templates/category-e-exception.md |

For data provider and decoration contract patterns, see references/common-patterns.md.

### Step 2: Replace Placeholders

- `{Module}` - Core module (e.g., `Content`, `Checkout`, `System`)
- `{Submodule}` - Submodule path (e.g., `Product`, `Cart\LineItem`)
- `{TargetClass}` - Class name being tested
- `{Entity}` - Entity name for DAL tests
- `{Method}` - Method name being tested
- `{Expected}` - Expected outcome description
- `{Condition}` - Condition description
- `{Exception}` - Exception class name

### Step 3: Write Test File

Write to correct location: `tests/unit/{path matching src}/{ClassName}Test.php`

---

## Phase 4: Validate and Fix

### Validation Loop

```
- [ ] PHPStan passes (0 errors)
- [ ] PHPUnit passes (all tests green)
- [ ] ECS passes (code style)
```

### Step 1: Run PHPStan

```json
{
  "paths": ["tests/unit/Path/To/GeneratedTest.php"],
  "error_format": "json"
}
```

Zero errors = pass.

### Step 2: Fix PHPStan Errors

Apply fixes for common errors. See references/validation-error-mapping.md.

### Step 3: Run PHPUnit

```json
{
  "paths": ["tests/unit/Path/To/GeneratedTest.php"],
  "output_format": "result-only"
}
```

All tests passing = success. If tests fail, re-run without `output_format` to get failure details for Step 4.

### Step 4: Fix Test Failures

Apply fixes for common failures. See references/validation-error-mapping.md.

### Step 5: Run ECS Check and Fix

Check for violations, then apply fixes if needed.

### Repeat Until Pass

Loop through Steps 1-5 until all validations pass.

**Maximum iterations**: Stop after 3 failed attempts and proceed to Phase 5.

---

## Phase 5: Generate Report

For output format and examples, see references/output-format.md.

### Status Determination

| Condition | Status | skip_type |
|-----------|--------|-----------|
| All validations pass | SUCCESS | — |
| Test generated, validation issues remain after 3 iterations | PARTIAL | — |
| File excluded from coverage in phpunit.xml.dist | SKIPPED | `coverage_excluded` |
| No testable logic (per Test Requirement Rules) | SKIPPED | `no_logic` |
| Invalid input (not a PHP class, file not found) | FAILED | — |

### Report Contents

1. **Summary**: Source path, test path, status, category
2. **Generation Details**: Test method count, template used
3. **Validation Results**: PHPStan/PHPUnit/ECS pass/fail counts
4. **Remaining Issues** (if PARTIAL): Location, error, status table

---

## Additional Resources

### Reference Files

For detailed patterns and techniques, consult:

- **references/test-requirement-rules.md** - Decision tree for what to test
- **references/category-detection.md** - How to categorize source classes
- **references/essential-rules.md** - Naming, attribute, structure rules
- **references/validation-error-mapping.md** - Error codes and fixes
- **references/shopware-stubs.md** - StaticEntityRepository, Generator patterns
- **references/exception-patterns.md** - expectExceptionObject, expectException + message, exception codes
- **references/mocking-patterns.md** - createStub vs createMock, intersection types, configuration, side-effect verification
- **references/deprecation-guards.md** - DisabledFeatures, skipTestIfActive/InActive, Feature::silent, class-level guards
- **references/common-patterns.md** - Data providers, AAA structure, event subscribers, decoration pattern
- **references/output-format.md** - Report output contract

### Templates

Category-specific test generation templates in `templates/`:

- **category-a-dto.md** - Simple DTO/Entity tests

Related in AI Agents