builtbyberry/laravel-swarm-installer-testkit
A testing toolkit for the Laravel Swarm installer, providing helpers and fixtures to verify installation flows, file scaffolding, and command behavior in your test suite with confidence.
This is a dev-only package. It ships no runtime code and should never appear in require. Its job is to give every package in the Swarm family (core laravel-swarm plus companions) the same Orchestra Testbench scaffolding for testing swarm:install* Artisan commands, so an installer test written in one repo looks and behaves like the same test in another.
Install it:
composer require --dev builtbyberry/laravel-swarm-installer-testkit
Then check two things in composer.json before writing a line of test code:
require-dev block of the testkit itself — the Testbench major version it pins must match the Laravel version you support. Testbench majors are tightly coupled to Laravel majors, and a mismatch produces confusing "class not found" or container errors rather than a clean failure.phpunit.xml.dist / pest.php — your suite must point at the harness base class, not Orchestra\Testbench\TestCase directly, or you'll silently lose every helper the package provides.Where to look first:
src/ for the base TestCase and any traits (typically something like InteractsWithSwarmInstaller or InstallerAssertions).stubs/ or fixtures/ directory — this is where the sandbox skeleton app and canned installer output live.tests/ directory. A test harness is best documented by its self-test; the testkit's own suite is the working example of every helper it exposes.First use case: prove that php artisan swarm:install publishes a config file and registers the service provider without shelling out to Composer or writing into your real vendor/ or config/ directories. Something like:
use BuiltByBerry\SwarmInstallerTestkit\TestCase;
final class InstallCommandTest extends TestCase
{
public function test_it_publishes_config(): void
{
$this->artisan('swarm:install', ['--no-interaction' => true])
->assertExitCode(0);
$this->assertFileExists(config_path('swarm.php'));
}
}
Names above follow the conventions this style of harness uses — confirm the exact base class namespace and helper names against src/ on first install, since at 0 stars there's no ecosystem of blog posts to fall back on.
Wire the harness in once, per package. Create a thin tests/TestCase.php that extends the testkit base and declares your provider. Every installer test then extends that, giving you one place to bump when Testbench majors change:
namespace BuiltByBerry\Swarm\Tests;
use BuiltByBerry\SwarmInstallerTestkit\TestCase as InstallerTestCase;
abstract class TestCase extends InstallerTestCase
{
protected function getPackageProviders($app): array
{
return [\BuiltByBerry\Swarm\SwarmServiceProvider::class];
}
}
For Pest, bind it globally instead: uses(Tests\TestCase::class)->in('Feature/Install').
Fake the process layer, always. Installer commands are wrappers around composer require, npm install, and php artisan vendor:publish. The single most valuable thing this harness buys you is a consistent way to intercept those calls (Laravel's Process::fake() or an equivalent binding) so tests are hermetic and fast. Set the fake in setUp() before the command runs, and assert on the recorded invocations rather than just the exit code — "exit 0 but ran composer require against the live network" is the failure mode you're guarding against.
Drive flags through data providers. Installers exist to handle permutations: --force over an existing config, --no-interaction in CI, re-running when already installed, missing optional dependencies. A provider over [flags, preconditions, expected artifacts] keeps the matrix readable and is the natural shape for this harness:
#[DataProvider('flagMatrix')]
public function test_install_respects_flags(array $flags, array $expectedFiles): void { /* ... */ }
Reuse the same harness across the family. The whole point of the package is that laravel-swarm, swarm-queue, swarm-tenancy, etc. all assert installation the same way. Keep your per-package TestCase a pure declaration (providers + fixtures) and push any shared custom assertion into the testkit itself via a PR, or into a local trait if it's genuinely package-specific. Resist copy-pasting assertion helpers between repos — that's the drift this package exists to prevent.
Version the testkit in lockstep with the installer. Because the harness encodes assumptions about what swarm:install produces (config keys, migration names, published tag names), treat a bump of the install command and a bump of the testkit as one atomic change across repos. A composer update in one companion repo that pulls a newer testkit than its siblings is the most likely source of "works in core, red in companion" CI noise.
Add a CI matrix job that runs the suite against each supported Laravel version. Testbench makes this cheap (ORCHESTRA_TESTBENCH / laravel/framework constraint per job), and installer commands are exactly the kind of code that breaks on framework majors due to signature changes in Process, Filesystem, and the Command lifecycle.
2026-09-03 release date with 0 stars and 0 dependents means you are effectively the first consumer. Use an explicit version constraint or a commit SHA in require-dev, and vendor/fork if the repo goes quiet; there's no community to notice a break.--no-interaction is mandatory. Installer commands love $this->confirm(). Without -n (or the array equivalent ['--no-interaction' => true]), tests hang in CI until the job timeout rather than failing with a useful message.Process in a singleton or bootstrapping callback. A fake set in the test body arrives too late and the real binary runs.config_path() or base_path() inside the sandbox persist for the life of the process unless you reset them. Clean up in tearDown() (or override the harness's reset hook) or a later test in the same class will see artifacts from an earlier one and produce order-dependent green/red.File::fake() doesn't help here. It fakes the storage disks, not real paths an installer writes to. Use the sandbox's actual temp directory and assert on real file_exists()/file_get_contents().bootstrap/cache/*, and the vendor manifest can be cached from a previous test's install run, making a "fresh install" test pass for the wrong reason.composer.json carries post-autoload-dump hooks, faking the process layer may not stop them from firing during bootstrapping. Keep the sandbox composer.json minimal.->dump() (or ->expectsOutputToContain(...)) inside $this->artisan(...) when an assertion on published files fails but the exit code is 0 — installers commonly swallow exceptions and report success.defineEnvironment() (env/config for the sandbox), getPackageProviders(), and the fixture/sandbox-path accessor. If you find yourself overriding more than two or three methods, you're fighting the harness — check whether a testkit PR would be the better fix.How can I help you explore Laravel packages today?