Useful 'Architecture test' code snippets for PEST which you can use in your Laravel projects.
PR and ideas are welcome! 🙌
- Do not leave debug statements
- Do not use
env()
outside of config files - Use strict type checking
- Invokable classes should have __invoke method
- Classes should contain a specific method
- Classes should implement an interface
- Classes should have proper suffix
- Ensure cross domain boundaries are respected
- Do not access session data in Async jobs
It's easy to leave debug statements like dd
or dump
in the code and push them to production.
But now we can avoid that by adding the following test.
arch('Do not leave debug statements')
->expect(['dd', 'dump', 'var_dump'])
->not->toBeUsed();
If you are using Ray you should add ray
to the expectation list as well.
If we use config:cache
in production (which we should), env()
helper will always return null
.
But it's very easy to forget while writing code!
Add the following test to make sure we are not doing that.
arch('Do not use env helper in code')
->expect(['env'])
->not->toBeUsed();
We might prefer to use Strict type checking in our code.
To ensure that, we can add the following test:
arch('Use string type check')
->expect('App')
->toUseStrictTypes();
Sometimes we want to make sure that some classes should be invokable. For example, some developers prefer that Action
classes should be invokable.
To ensure that, we can add the following test:
arch('Action classes should be invokable')
->expect('App\Actions')
->toBeInvokable();
A lot of Laravel developers prefer to useLaravel actions. This classes must have a handle
method. Also, [Laravel Jobs] should have a handle
method as well.
Using the following test, we can make sure that these classes must have the handle
method implemented:
arch('Job classes should have handle method')
->expect('App\Jobs')
->toHaveMethod('handle');
When implementing the Repository pattern, it's a common practice to have a RepositoryInterface
and all the Repository classes implement it.
How to make sure no Repository classes missed this requirement? It's pretty easy:
arch('app')
->expect('App\Repositories')
->toImplement('App\Repositories\RepositoryInterface');
Service pattern is also a popular choice by the Laravel developers. In this case, we might want to make sure that all the 'Service' classes have the 'Service' suffix in it.
To confirm this, we can add this test:
arch('Services classes should have proper suffix')
->expect('App\Services')
->toHaveSuffix('Service');
We do the same for 'Controller' classes, right?
arch('Controller classes should have proper suffix')
->expect('App\Controllers')
->toHaveSuffix('Controller');
When implementing 'Domains' or 'Modules' in our code, we might want to make sure that one domains/modules code is not directly used in other domains/modules. This will help us to keep the domains/modules independent of each other.
For example, let's say we have two Modules in our project called RideSharing
and FoodDelivery
. We want to make sure that one modules code is not used in other module.
To ensure this constraint, we can just add this:
arch('Modules should be independent')
->expect('Modules\RideSharing')
->not->toBeUsed('Modules\FoodDelivery');
In asynchronous jobs, session data is not available. Because we process asynchronous jobs with Queue workers later.
This is why it's a good practice to avoid using helpers like, session
, request
, auth
etc. in our asynchronous jobs.
To ensure this constraint, we can just add this:
arch('Do not access session data in Async jobs')
->expect([
'session',
'auth',
'request',
'Illuminate\Support\Facades\Auth',
'Illuminate\Support\Facades\Session',
'Illuminate\Http\Request',
'Illuminate\Support\Facades\Request'
])
->each->not->toBeUsedIn('App\Jobs');