Documentation Testing
This guide explains how to write documentation with testable code examples and how to run the documentation tests.
All PHP code examples in the documentation are automatically tested to ensure they work correctly. This helps maintain high-quality documentation that stays in sync with the codebase.
Running Documentation Tests
Section titled “Running Documentation Tests”Quick Start
Section titled “Quick Start”Run all documentation tests using the Task command:
task test:documentationOr using the short alias:
task test:docsWhat Gets Tested
Section titled “What Gets Tested”The documentation tests include:
- All Markdown files in starlight/src/content/docs/(viaStarlightAllExamplesTest)
- README.md examples (via ReadmeExamplesTest)
- Examples directory (examples/) files (viaExamplesTest)
- Specific class examples (DataAccessor, DataFilter, DataMapper tests)
Manual Execution
Section titled “Manual Execution”You can also run the tests directly with Pest:
# Run all documentation tests (using testsuite)docker exec data-helpers-php84 vendor/bin/pest --testsuite=Documentation
# Or using the docs groupdocker exec data-helpers-php84 vendor/bin/pest --group=docsWith a specific PHP version:
docker exec data-helpers-php82 vendor/bin/pest --testsuite=Documentationdocker exec data-helpers-php83 vendor/bin/pest --testsuite=DocumentationWriting Testable Documentation
Section titled “Writing Testable Documentation”Basic PHP Code Blocks
Section titled “Basic PHP Code Blocks”Use standard PHP code blocks with the php language identifier:
```phpuse event4u\DataHelpers\DataAccessor;
$data = ['name' => 'John', 'age' => 30];$accessor = new DataAccessor($data);
$name = $accessor->get('name');// $name is 'John'```Code Block Requirements
Section titled “Code Block Requirements”For code to be tested, it must:
- Be executable - Not just class/interface/trait declarations
- Be complete - No placeholders like ...or// ...
- Have proper imports - Include all necessary usestatements
- Not require external dependencies - Use only classes from the package
Examples That Are Automatically Skipped
Section titled “Examples That Are Automatically Skipped”The test system automatically skips code blocks that contain:
- Placeholders: ...or// ...
- Class declarations: class MyClass,interface MyInterface,trait MyTrait,enum MyEnum
- Property-only code: Only property declarations without executable code
- Incomplete arrays: Lines ending with =>
- External dependencies: References to Spatie\,extends Model, etc.
- Specific files: architecture.md,contributing.md,migration-from-spatie.md
Skipping Examples
Section titled “Skipping Examples”Method 1: HTML Comment (Recommended)
Section titled “Method 1: HTML Comment (Recommended)”Add an HTML comment before the code block:
<!-- skip-test -->```php// This example will not be tested$result = someUndefinedFunction();```Method 2: Inline Marker
Section titled “Method 2: Inline Marker”Add skip-test after the language identifier:
```php skip-test// This example will not be tested$result = someUndefinedFunction();```When to Skip Examples
Section titled “When to Skip Examples”Skip examples when:
- Showing incomplete code for illustration purposes
- Demonstrating error cases that would fail intentionally
- Using external dependencies not available in tests
- Showing framework-specific code that requires a full framework setup
- Illustrating concepts without executable code
Best Practices
Section titled “Best Practices”1. Make Examples Self-Contained
Section titled “1. Make Examples Self-Contained”Each example should be complete and runnable:
use event4u\DataHelpers\DataMapper;
// ✅ Good - Complete example$result = DataMapper::source(['name' => 'John'])    ->template(['full_name' => '{{ name }}'])    ->map();
// ❌ Bad - Incomplete$result = DataMapper::source($data)  // Where does $data come from?    ->template($template)             // Where does $template come from?    ->map();2. Include All Imports
Section titled “2. Include All Imports”Always include necessary use statements:
// ✅ Gooduse event4u\DataHelpers\DataAccessor;use event4u\DataHelpers\DataMutator;
$accessor = new DataAccessor($data);$mutator = new DataMutator($data);
// ❌ Bad - Missing imports$accessor = new DataAccessor($data);  // Will fail: Class not found3. Use Assertions for Verification
Section titled “3. Use Assertions for Verification”Add assertions to verify the expected behavior:
use event4u\DataHelpers\DataAccessor;
$data = ['name' => 'John', 'age' => 30];$accessor = new DataAccessor($data);
$name = $accessor->get('name');assert($name === 'John');  // Verify the result4. Avoid External Dependencies
Section titled “4. Avoid External Dependencies”Don’t reference classes that aren’t part of the package:
// ❌ Bad - External dependencyuse App\Models\User;
$user = User::find(1);
// ✅ Good - Use package classes onlyuse event4u\DataHelpers\SimpleDto;
class UserDto extends SimpleDto{    public function __construct(        public readonly string $name,        public readonly int $age,    ) {}}5. Keep Examples Focused
Section titled “5. Keep Examples Focused”Each example should demonstrate one concept:
// ✅ Good - Focused on one featureuse event4u\DataHelpers\DataAccessor;
$data = ['user' => ['name' => 'John']];$accessor = new DataAccessor($data);$name = $accessor->get('user.name');
// ❌ Bad - Too many concepts at onceuse event4u\DataHelpers\DataAccessor;use event4u\DataHelpers\DataMutator;use event4u\DataHelpers\DataFilter;
$accessor = new DataAccessor($data);$mutator = new DataMutator($data);$filter = DataFilter::query($data);// ... too much happeningTroubleshooting
Section titled “Troubleshooting”Example Fails in Tests
Section titled “Example Fails in Tests”If an example fails during testing:
- Check the error message - The validation script shows detailed errors
- Run the example manually - Copy the code and run it in isolation
- Verify imports - Make sure all usestatements are present
- Check for typos - Variable names, method names, etc.
- Add skip-test - If the example is intentionally incomplete
Example Should Be Skipped But Isn’t
Section titled “Example Should Be Skipped But Isn’t”If an example should be skipped but is still being tested:
- Add explicit skip marker - Use <!-- skip-test -->orskip-testinline
- Check skip conditions - Review the automatic skip conditions above
- Verify file path - Some files are automatically skipped
All Examples in a File Fail
Section titled “All Examples in a File Fail”If all examples in a file fail:
- Check file encoding - Must be UTF-8
- Verify code block syntax - Must use ```php(with backticks)
- Check for global issues - Missing autoload, wrong PHP version, etc.
Test Output
Section titled “Test Output”The validation script provides detailed output:
🔍 Validating all documentation examples...
✅ data-accessor.md: 15 executed, 3 skipped, 0 failed✅ data-mutator.md: 12 executed, 2 skipped, 0 failed❌ data-mapper.md: 18 executed, 4 skipped, 2 failed
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━📊 Summary━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Total files:     45Total examples:  523Executed:        487Skipped:         34Failed:          2Success rate:    99.6%Related
Section titled “Related”- Contributing Guide - General contribution guidelines
- Development Setup - Setting up your development environment
- Test Matrix - Understanding the test matrix
