Testing
Testing
Guide to running tests and understanding test coverage.
Running Tests
Quick Test Run
composer test
This runs the full test suite with Pest.
With Coverage Report
composer test-coverage
Generates HTML coverage report in coverage/ directory.
Specific Test File
vendor/bin/pest tests/Unit/Parser/LinuxProcStatParserTest.php
CI Mode (Strict Output)
vendor/bin/pest --ci
Test Structure
tests/
├── Unit/ # Unit tests
│ ├── DTO/ # DTO tests
│ ├── Parser/ # Parser tests
│ └── Support/ # Support class tests
├── ExampleTest.php # Integration tests
└── ArchTest.php # Architecture rules
Test Categories
Unit Tests (tests/Unit/)
Test individual components in isolation:
- Parsers (LinuxProcStatParser, etc.)
- DTOs (CpuTimes, MemorySnapshot, etc.)
- Support classes (FileReader, ProcessRunner, etc.)
Integration Tests (ExampleTest.php)
Test the full stack on real system APIs:
- Actually reads from
/proc,/sys, system commands - Tests on the machine running the tests
- Platform-specific (tests run on Linux CI, macOS locally)
Architecture Tests (ArchTest.php)
Enforce architectural rules:
- No debugging functions (dd, dump, ray)
- Consistent code organization
- Naming conventions
Coverage
Current coverage: 89.9% (94 tests, 238 assertions)
Coverage by Component
- Parsers: 95%+
- DTOs: 100%
- Support classes: 90%+
- Linux sources: ~80% (limited on macOS CI)
- macOS sources: ~50% (limited on Linux CI)
Why Not 100%?
Platform-specific code cannot be tested on both platforms simultaneously:
- Linux sources: 0% coverage when tests run on macOS
- macOS sources: 0% coverage when tests run on Linux
This is expected and acceptable.
Writing Tests
Pest Syntax
This project uses Pest v4:
it('parses CPU times correctly', function () {
$input = "cpu 100 0 50 200 0 0 0 0 0 0";
$times = LinuxProcStatParser::parseCpuLine($input);
expect($times->user)->toBe(100);
expect($times->system)->toBe(50);
expect($times->idle)->toBe(200);
});
Testing with Result<T>
it('returns success result', function () {
$result = SystemMetrics::cpu();
expect($result->isSuccess())->toBeTrue();
expect($result->getValue())->toBeInstanceOf(CpuSnapshot::class);
});
it('handles failure gracefully', function () {
// Set up failure condition
$result = SystemMetrics::cpu();
expect($result->isFailure())->toBeTrue();
expect($result->getError())->toBeInstanceOf(Throwable::class);
});
Testing DTOs
it('calculates totals correctly', function () {
$times = new CpuTimes(100, 50, 200, 0, 0, 0, 0, 0);
expect($times->total())->toBe(350);
expect($times->busy())->toBe(150);
});
Code Quality
Static Analysis
composer analyse
Runs PHPStan at Level 9 (strictest).
Code Style
composer format
Formats code with Laravel Pint (PSR-12).
Continuous Integration
GitHub Actions runs:
- Tests on Ubuntu (Linux) and macOS
- PHP 8.3 and 8.4
- Automatic code formatting
- PHPStan analysis
- Coverage generation
Best Practices
- Test behavior, not implementation
- Use descriptive test names
- Keep tests independent (no shared state)
- Mock external dependencies (files, commands)
- Test edge cases (empty values, errors, boundaries)
- Maintain high coverage (aim for >85%)
Related Documentation
- Contributing - Contribution guidelines
- Architecture - Design principles