Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Pest Plugin Arch
https://github.com/pestphp/pest-plugin-arch
Admin
Pest Plugin Arch is a plugin for the Pest testing framework that allows for architectural testing of
...
Tokens:
3,488
Snippets:
23
Trust Score:
8.4
Update:
5 months ago
Context
Skills
Chat
Benchmark
91.2
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Pest Plugin Arch Pest Plugin Arch is an architecture testing plugin for Pest PHP that enables developers to enforce architectural rules and constraints in their PHP projects. It provides a fluent API for defining and validating architectural boundaries, dependency rules, and code organization patterns. The plugin helps maintain clean architecture by catching architectural violations early in the development process through automated tests. This plugin extends Pest's testing framework with specialized expectations for checking class relationships, dependency directions, naming conventions, and layered architecture compliance. It integrates seamlessly with Pest's existing test syntax and leverages PHP's reflection capabilities along with static analysis to verify that your codebase adheres to defined architectural principles. The plugin supports testing classes, interfaces, traits, enums, and even user-defined functions. ## Core API Functions ### arch() - Define Architecture Tests Creates an architecture test with automatic grouping under the 'arch' test group. ```php <?php use Tests\Fixtures\Controllers\ProductController; use Tests\Fixtures\Controllers\UserController; // Basic architecture test arch('controllers should be final') ->expect('App\Controllers') ->classes->toBeFinal(); // Test with multiple expectations chained arch('strict types') ->expect('*') ->toUseStrictTypes(); // Complex architecture rule arch('base') ->expect('Pest\Arch') ->classes->toBeFinal() ->classes->not->toBeAbstract() ->toOnlyUse([ 'Pest', 'PHPUnit\Architecture', 'Symfony\Component\Finder\Finder', ])->ignoring(['PHPUnit\Framework', 'Composer']); ``` ### expect()->toOnlyUse() - Enforce Dependency Constraints Ensures a class or namespace only depends on specified dependencies. ```php <?php use Tests\Fixtures\Models\Product; use Tests\Fixtures\Contracts\Models\Fooable; use Tests\Fixtures\Contracts\Models\Storable; // Single class dependency check expect(Product::class) ->toOnlyUse([Fooable::class, Storable::class]); // Namespace-level dependency check expect('Tests\Fixtures\Models') ->toOnlyUse('Tests\Fixtures\Contracts\Models'); // Allow nothing except specified dependencies expect('Pest\Arch\Collections') ->toOnlyUse('Pest\Arch\ValueObjects'); // With ignoring specific dependencies expect(Product::class) ->toOnlyUse([]) ->ignoring('Tests\Fixtures\Contracts'); // Opposite assertion expect('Pest\Arch\Exceptions') ->not->toOnlyUse(['InvalidDependency']); ``` ### expect()->toUse() - Check Dependency Usage Verifies that a class or namespace uses specific dependencies. ```php <?php use Tests\Fixtures\Enums\Color; use Tests\Fixtures\Enums\ColorThatDependsOnColor; use Tests\Fixtures\Misc\DependsOnVendor; // Check class uses specific dependency expect(ColorThatDependsOnColor::class) ->toUse([Color::class]); // Check vendor dependency usage expect(DependsOnVendor::class) ->toOnlyUse('Pest') ->toOnlyUse('Pest\Support') ->toOnlyUse('Pest\Support\Str'); // Check global function usage expect('Tests\Fixtures\Misc\DependOnGlobalFunctions') ->toUse('my_request_global_function') ->not->toUse('Tests\Fixtures\my_request_namespaced_function'); // Check namespaced function usage expect('Tests\Fixtures\Misc\DependOnNamespacedFunctions') ->toUse('Tests\Fixtures\my_request_namespaced_function') ->not->toUse('my_request_global_function'); ``` ### expect()->toUseNothing() - Enforce Zero Dependencies Asserts that a class or namespace has no external dependencies. ```php <?php use Tests\Fixtures\Support\Collection; use Tests\Fixtures\Enums\Color; // Verify class has no dependencies expect(Collection::class) ->toUseNothing(); // Verify namespace has no dependencies expect('App\Repositories') ->toUseNothing(); // Enum with no dependencies expect(Color::class) ->toUseNothing(); // With exceptions using ignoring expect('Pest\Arch\ValueObjects') ->toUseNothing() ->ignoring(['PHPUnit\Framework', 'Pest\Expectation']); ``` ### expect()->toImplement() - Check Interface Implementation Verifies that classes implement specific interfaces. ```php <?php use Tests\Fixtures\Contracts\Controllers\Indexable; use Tests\Fixtures\Controllers\UserController; use Tests\Fixtures\Controllers\ProductController; // Single class implements interface expect(UserController::class) ->toImplement(Indexable::class); // Negative assertion expect(ProductController::class) ->not->toImplement(Indexable::class); // Namespace-level check with ignoring expect('Tests\Fixtures\Controllers') ->toImplement(Indexable::class) ->ignoring(ProductController::class); // Check exceptions implement Throwable expect('Pest\Arch\Exceptions') ->toImplement(Throwable::class); ``` ### expect()->toExtend() - Check Class Inheritance Ensures classes extend a specific parent class. ```php <?php use Tests\Fixtures\Controller; use Tests\Fixtures\Controllers\UserController; use Tests\Fixtures\Controllers\ProductController; // Single class extends parent expect(UserController::class) ->toExtend(Controller::class); // Negative assertion expect(ProductController::class) ->not->toExtend(Controller::class); // Namespace check with ignoring expect('Tests\Fixtures\Controllers') ->toExtend(Controller::class) ->ignoring(ProductController::class); ``` ### expect()->toExtendNothing() - Enforce No Inheritance Verifies that classes don't extend any parent class. ```php <?php use Pest\Arch\Objects\ObjectDescription; use Pest\Arch\Objects\VendorObjectDescription; // Classes should not extend anything expect('Pest\Arch') ->classes->toExtendNothing() ->ignoring([ VendorObjectDescription::class, ObjectDescription::class, ]); // Namespace-level check expect('App\ValueObjects') ->toExtendNothing(); ``` ### expect()->toBeFinal() - Enforce Final Classes Checks that classes are declared as final. ```php <?php use Tests\Fixtures\Controllers\ProductController; use Tests\Fixtures\Controllers\UserController; // Single class is final expect(ProductController::class) ->toBeFinal(); // Negative assertion expect(UserController::class) ->not->toBeFinal(); // Namespace-level check expect('Tests\Fixtures\Controllers') ->toBeFinal() ->ignoring(UserController::class); // All classes in namespace are final expect('Pest\Arch') ->classes->toBeFinal(); ``` ### expect()->toHaveSuffix() - Enforce Naming Conventions Verifies that classes follow naming suffix conventions. ```php <?php use Tests\Fixtures\Controllers\UserController; // Namespace follows naming convention expect('Tests\Fixtures\Controllers') ->toHaveSuffix('Controller'); // Single class has suffix expect(UserController::class) ->toHaveSuffix('Controller'); // Services namespace convention expect('App\Services') ->toHaveSuffix('Service'); // Repository naming pattern expect('App\Repositories') ->toHaveSuffix('Repository'); ``` ### expect()->toBeUsedIn() - Check Usage by Other Classes Verifies that a class or interface is used in specific locations. ```php <?php // Check where a function is used expect('sleep') ->toBeUsedIn('Tests') ->not->toBeUsedIn('App\Production'); // Check interface usage expect('Tests\Fixtures\Contracts\Models\Fooable') ->toBeUsedIn('Tests\Fixtures\Models'); // Check trait usage expect('App\Concerns\HasTimestamps') ->toBeUsedIn('App\Models'); ``` ### expect()->toOnlyBeUsedIn() - Restrict Usage Locations Ensures a class or interface is only used in specified locations. ```php <?php use Tests\Fixtures\Models\User; use Tests\Fixtures\Contracts\Models\Barable; // Interface only used in specific namespace expect(Barable::class) ->toOnlyBeUsedIn([ 'Tests\Fixtures\Models', 'Tests\Fixtures\Services', ]); // Restrict function usage expect('die') ->toOnlyBeUsedIn('Tests'); // Internal class usage restriction expect('App\Internal\Configuration') ->toOnlyBeUsedIn('App\Bootstrap'); ``` ### expect()->toBeUsedInNothing() - Enforce No Usage Verifies that a class, interface, or function is not used anywhere. ```php <?php use Tests\Fixtures\Contracts\Models\Fooable; // Class should not be used expect('Tests\Fixtures\Deprecated\OldService') ->toBeUsedInNothing(); // Function not used expect('sleep') ->toBeUsedInNothing(); // With ignoring specific usages expect(Fooable::class) ->toBeUsedInNothing() ->ignoring('Tests\Fixtures\Models'); // Alternative syntax expect(Fooable::class) ->not->toBeUsed(); ``` ### Filtering Modifiers - Target Specific Code Types Apply filters to narrow down architectural expectations to specific code types. ```php <?php use Tests\Fixtures\Contracts\Controllers\Indexable; use Tests\Fixtures\Controller; use Tests\Fixtures\HasResponses; // Test only classes extending a parent expect('Tests\Fixtures\Controllers') ->extending(Controller::class) ->toExtend(Controller::class); // Test only classes implementing an interface expect('Tests\Fixtures\Controllers') ->implementing(Indexable::class) ->toImplement(Indexable::class); // Test only classes using a trait expect('Tests\Fixtures\Controllers') ->using(HasResponses::class) ->toUseTrait(HasResponses::class); // Test only abstract classes expect('Tests\Fixtures\Misc\Abstracts') ->abstractClasses() ->toHaveMethod('edible'); // Test only interfaces expect('App') ->interfaces() ->toHaveSuffix('Interface'); // Test only traits expect('App\Concerns') ->traits() ->toHaveSuffix('Trait'); // Test only enums expect('App\Enums') ->enums() ->toUseNothing(); // Test only classes (not interfaces/traits) expect('Pest\Arch') ->classes->toBeFinal() ->classes->not->toBeAbstract(); ``` ### Wildcard Namespace Patterns - Test Multiple Layers Use wildcards to match multiple namespaces across different architectural layers. ```php <?php // Single wildcard - matches one level expect('Tests\Fixtures\Domains\*\Models') ->getTargets() ->toBe([ 'Tests\Fixtures\Domains\A\Models\Article', 'Tests\Fixtures\Domains\B\Models\Article', ]); // Multiple wildcards - matches multiple levels expect('Tests\Fixtures\Domains\*\*\Models') ->getTargets() ->toBe([ 'Tests\Fixtures\Domains\A\Contracts\Models\Bazable', 'Tests\Fixtures\Domains\B\Contracts\Models\Bazable', ]); // Enforce layer isolation with wildcards expect('App\Modules\*\Domain') ->toOnlyUse('App\Modules\*\Domain') ->not->toUse('App\Modules\*\Infrastructure'); ``` ### Global Configuration - Test-Wide Settings Configure global options that apply across all architecture tests. ```php <?php // In Pest.php - applies to all arch tests uses()->beforeEach(function () { $this->arch()->ignore([ 'NunoMaduro\Collision', 'Illuminate\Support', ]); })->in(__DIR__); // Ignore global functions in specific test expect('Tests\Fixtures\Misc\DependOnGlobalFunctions') ->not ->toUse('my_request_global_function') ->ignoringGlobalFunctions(); ``` ## Integration and Testing Patterns Pest Plugin Arch is designed to integrate seamlessly into your existing Pest test suite, providing architectural guardrails without disrupting your development workflow. Architecture tests are automatically grouped under the 'arch' tag, allowing you to run them separately or as part of your full test suite. The plugin works by analyzing your codebase using PHP reflection and static analysis, detecting dependencies through use statements, type hints, and instantiations. All violations are reported with precise file and line number information, making it easy to locate and fix architectural issues. The plugin excels at enforcing layered architecture patterns such as hexagonal architecture, onion architecture, and clean architecture. You can define strict boundaries between domain, application, and infrastructure layers, ensuring that dependencies flow in the correct direction. It supports modular monoliths by allowing wildcard patterns to check that modules remain isolated from each other. The fluent API enables both positive assertions (what code should do) and negative assertions (what code should not do), giving you complete control over your architectural rules. Combined with Pest's test organization features, you can create comprehensive architectural test suites that document and enforce your team's design decisions automatically.