moox/devtools
moox/devtools is a Laravel devtools package that streamlines local development with helpful utilities and tooling for debugging, scaffolding, and productivity. Built to integrate cleanly into Laravel apps and speed up everyday developer workflows.
Installation: Add the package to your Laravel project via Composer:
composer require --dev moox/devtools
This installs a curated set of dev tools including Pest, Larastan (PHPStan), Pint, and Livewire/Pest plugins.
First Use Case: Run your first Pest test to verify integration:
./vendor/bin/pest
If successful, you’ll see output like:
PASS Tests/Unit/ExampleTest.php
✓ it works (0.1s)
Where to Look First:
tests/Pest.php for plugin and preset settings..larastan.neon in the project root for static analysis rules..pint.php for code formatting preferences.{{ @capture }} in Blade templates for testing (documented in README.md if available).Testing with Pest:
tests/Unit/ with Pest’s fluent syntax:
test('user can be created', function () {
$user = User::factory()->create();
expect($user->email)->toBeValid();
});
tests/Feature/ with Laravel helpers:
test('GET /dashboard', function () {
$response = $this->get('/dashboard');
$response->assertStatus(200);
});
test('livewire component renders', function () {
Livewire::test(ProfileComponent::class)
->assertSee('Profile');
});
Static Analysis:
./vendor/bin/larastan analyse --level=5 app
./vendor/bin/larastan analyse --memory-limit=1G --error-format=json
Code Formatting:
./vendor/bin/pint --test # Dry run
./vendor/bin/pint # Apply fixes
composer.json scripts:
"scripts": {
"lint": "pint",
"test": "pest"
}
Blade Testing:
{{ @capture('header') }}
<h1>{{ $title }}</h1>
{{ @endcapture }}
test('blade header', function () {
BladeCapture::capture('header', ['title' => 'Test']);
expect(BladeCapture::get('header'))->toContain('Test');
});
Laravel Projects:
pest-plugin-laravel for seamless Laravel integration (e.g., actingAs, assertDatabaseHas).phpunit.xml for legacy tests:
<testsuites>
<testsuite name="Unit">
<directory>./tests/Unit</directory>
</testsuite>
</testsuites>
Livewire:
test('livewire emits events', function () {
Livewire::test(Counter::class)
->emit('increment')
->assertEmitted('increment');
});
CI/CD:
name: Laravel Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-php@v3
with:
php-version: '8.2'
- run: composer install --dev
- run: ./vendor/bin/pest
- run: ./vendor/bin/larastan analyse --level=5 app
- run: ./vendor/bin/pint --test
Customization:
.pint.php, .larastan.neon).Version Lock-in:
composer.json.replace in composer.json to force a version:
"replace": {
"pestphp/pest": "6.0"
}
Larastan Strictness:
.larastan.neon:
ignores = [
'app/Helpers/*',
'app/Models/Concerns/*'
]
Pest Plugin Conflicts:
pest-plugin-livewire may conflict with custom Pest configurations. Ensure tests/Pest.php is minimal:
uses(Tests\TestCase::class)->in('Feature');
uses(PestPluginLivewire::class)->in('Feature');
Blade Capture Limitations:
@capture directive requires Laravel’s Blade compiler. Gotcha: Fails in non-Laravel contexts (e.g., CLI scripts).BladeCapture::capture() in tests instead:
BladeCapture::capture('template', ['data' => $data]);
Testbench vs. Pest:
TestCase from Testbench and Pest can cause conflicts. Stick to one:
// Pest
use Tests\TestCase;
class MyTest extends TestCase { ... }
// Testbench (legacy)
use Orchestra\Testbench\TestCase;
class LegacyTest extends TestCase { ... }
Pint Presets:
.pint.php:
return Pint::laravel()->withRules([
Pint\Rule\NoTrailingWhitespace::class,
]);
Pest:
./vendor/bin/pest --debug
bootstrap/Pest.php.Larastan:
./vendor/bin/larastan baseline app
--error-format=json for CI-friendly parsing.Composer Conflicts:
composer why-not moox/devtools
composer why pestphp/pest
Custom Pest Plugins:
composer.json and register in tests/Pest.php:
uses(MyCustomPlugin::class)->in('Unit');
Larastan Rules:
.larastan.neon:
extends = [larastan/laravel.neon]
rules = [
\Moox\CustomRule::class
]
Pint Rules:
.pint.php:
return Pint::laravel()->withRules([
Pint\Rule\NoTrailingWhitespace::class,
Pint\Rule\LineEnding::class,
]);
Testbench Extensions:
tests/CreatesApplication.php:
protected function getPackageProviders($app) {
return [
\Moox\YourPackage\ServiceProvider::class,
];
}
Git Hooks:
.git/hooks/pre-commit:
#!/bin/sh
./vendor/bin/pint --test || exit 1
./vendor/bin/larastan analyse --level=5 --error-format=json | jq -e '. >/dev/null' || exit 1
How can I help you explore Laravel packages today?