Released: Pyrameter 0.0.1
Pyrameter 0.0.1 — Initial Release
Pyrameter is a PHPUnit extension that measures the shape of your test suite.
It classifies tests by the classes and namespaces each test file consumes, then prints a pyramid shape report after PHPUnit runs.
Use it to spot when your suite is getting heavier, agree on what "healthy" means for your project, and optionally fail CI when the pyramid drifts too far.
Installation
composer require --dev boundwize/pyrameterQuick start
Register the PHPUnit extension:
<extensions>
<bootstrap class="Pyrameter\Extension"/>
</extensions>Then run PHPUnit as usual:
vendor/bin/phpunitAfter the test run, Pyrameter prints a summary of your suite shape:
Pyrameter
=========
Shape: Integration Mountain
Result: Violated ⚠
Kind Tests Actual Target Status
Unit 39 65.0% >= 70.0% ✗
Functional 10 16.7% <= 20.0% ✓
Integration 9 15.0% <= 8.0% ✗
E2E 1 1.7% <= 2.0% ✓
Unknown 1 1.7% <= 2.0% ✓
Total: 60 tests
Your suite is getting heavier.How classification works
Pyrameter does not trust test directories.
It does not scan production classes.
Instead, it classifies tests by configured class or namespace usage in test files:
- no configured heavy usage => unit
- framework test runtime => functional
- real resource boundary usage, such as database, cache, queue, filesystem, or external service => integration
- browser driver usage => e2e
When multiple usages match, the heaviest kind wins.
Mocked heavy dependencies stay unit.
Configuration
If no config path is provided, Pyrameter looks for pyrameter.php in the current working directory.
Start with defaults() to keep the built-in rules for common heavy usage, then tune the target shape for your project:
<?php
declare(strict_types=1);
use Pyrameter\Config\PyrameterConfig;
use Pyrameter\TestKind;
return PyrameterConfig::defaults()
->usesClass(App\Analyser\Analyser::class, TestKind::Integration)
->usesNamespace('App\Tests\Browser\\', TestKind::E2E)
->targetShape(
unit: ['min' => 75],
functional: ['max' => 15],
integration: ['max' => 7],
e2e: ['max' => 2],
unknown: ['max' => 1],
);Use create() when you want full control with no built-in usage rules:
<?php
declare(strict_types=1);
use Pyrameter\Config\PyrameterConfig;
use Pyrameter\TestKind;
return PyrameterConfig::create()
->usesClass(PDO::class, TestKind::Integration)
->usesNamespace('Doctrine\DBAL\\', TestKind::Integration)
->usesNamespace('Symfony\Bundle\FrameworkBundle\Test\\', TestKind::Functional)
->usesNamespace('Symfony\Component\Panther\\', TestKind::E2E)
->usesNamespace('Facebook\WebDriver\\', TestKind::E2E)
->targetShape(
unit: ['min' => 70],
functional: ['max' => 18],
integration: ['max' => 8],
e2e: ['max' => 2],
unknown: ['max' => 2],
);CI enforcement
By default, Pyrameter is report-only.
It prints target violations without changing PHPUnit's exit code.
When you are ready to enforce the shape in CI:
return PyrameterConfig::defaults()
->failOnViolation();Requirements
Pyrameter supports PHP 8.2+ and PHPUnit 11 or 12.
Notes
This is an early 0.0.1 release. The API should be considered unstable — breaking changes may occur in minor versions before 1.0.0.
Feedback, issues, and contributions are very welcome at https://github.com/boundwize/pyrameter
