orchestra/lumenate
Orchestra Lumenate bridges Laravel's Illuminate ecosystem with Lumen, providing a compatibility layer for familiar service providers, configuration, and helpers. It helps developers reuse Laravel-style components in lightweight Lumen applications without pulling in the full framework.
Before writing any code, check what version of illuminate/console your application already resolves to:
composer show illuminate/console | grep versions
v1.3.0 replaces the direct symfony/console requirement with illuminate/console. In a Laravel app this is almost always a no-op; in a Lumen app, or in a package that pulls this one in as a dependency, it changes which console version constraints are allowed. Confirm the resolved major matches your framework version before you go further.
Minimal setup
composer require --dev vendor/package:^1.3
bootstrap/app.php:
$app->register(Vendor\Package\PackageServiceProvider::class);
Lumen has no artisan binary by default, so you also need the console kernel booted in your artisan script (see Gotchas).php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider"
Where to look first
composer.json → require block of the package. v1.3.0's illuminate/console constraint tells you the oldest Laravel/Lumen you can pair it with.$this->commands([...]).TestCase / testing namespace → this is the primary surface area for most users, especially now that orchestra/lumen v5 is installed by default for fresh installs.First use case
Write one trivial command and one test that asserts its output. That exercises both halves of the release in under five minutes: the illuminate/console binding (command definition, output helpers) and the orchestra/lumen scaffold (test case bootstrapping). If that pair runs green, the rest is incremental.
Register commands declaratively, not ad hoc. In Laravel, add classes to App\Console\Kernel::$commands or call $this->commands([...]) from a provider. In Lumen, do it from your registered provider. Avoid Artisan::starting() closures for anything you intend to test — they are invisible to $this->artisan() assertions.
Use constructor/method injection in handle(). illuminate/console resolves command dependencies out of the container the same way controllers do:
public function handle(ReportBuilder $builder): int
{
$builder->forRange($this->argument('from'), $this->option('to'));
$this->info('Report written.');
return self::SUCCESS;
}
Define signatures with the fluent string and let the package's test harness assert them. Command signatures are cheap to test and catch option-registration mistakes early:
$this->artisan('reports:build', ['from' => '2024-01-01', '--to' => '2024-02-01'])
->expectsOutput('Report written.')
->assertExitCode(0);
For package authors, use the orchestra-based test harness for CI. Extend the provided TestCase, declare getPackageProviders() and getEnvironmentSetUp(), and keep a phpunit.xml.dist that mirrors your matrix. The orchestra/lumen v5 default is what makes the Lumen-side bootstrapping work without hand-rolling an application stub for every test class.
Split runtime from test dependencies. If you only need the console commands at runtime, keep the package in require and the orchestra/lumen-driven scaffolding in require-dev. Mixing them forces test benches into production installs.
Integration tip: assert output loosely. Prefer expectsOutputToContain() where available; exact-string assertions break the moment someone adds a trailing period or a color tag.
The v1.3.0 dependency swap is a minor version bump with a deceptively breaking edge. Swapping symfony/console for illuminate/console is fine for typical usage — Illuminate\Console\Command extends Symfony\Component\Console\Command\Command, so type-hints against Symfony types still resolve. It breaks when:
symfony/console directly at a version that now conflicts with the illuminate/console your app resolves. Run composer why symfony/console to see who is holding it.handle() or interact(). Prefer Illuminate's $this->info(), $this->error(), $this->choice(), $this->confirm(), and the Command::SUCCESS / Command::FAILURE constants.Application — the package now pulls in the Illuminate wrapper, and behavior around runningInConsole(), verbosity, and signal handling differs slightly.orchestra/lumen v5 is installed by default for fresh installs only. Existing lockfiles keep their previous version. After upgrading, force the resolution:
composer update vendor/package --with-dependencies
Then verify the Lumen line v5 targets actually matches your application. An orchestrator pinned to the wrong framework major produces confusing assertion failures rather than a clear version error.
Lumen's missing artisan binary is the number-one support issue. If commands are "not found," check that your artisan script instantiates Illuminate\Contracts\Console\Kernel, calls handle() with the raw $argv slice, and that the kernel's $commands array is populated. Facades may need $app->withFacades() enabled depending on how the package's commands are written.
Output capture in tests swallows everything. dump(), dd(), and direct echo calls do not appear in test output and will not satisfy expectsOutput(). Route everything through $this->line() / $this->output->writeln().
Debugging a failing command: run it from the CLI with -vvv before writing a test. Verbosity flags propagate through Illuminate\Console\OutputStyle, and --verbose is often the fastest way to see which service provider failed to register the command at all versus which command ran and failed.
Exit codes are meaningful. Returning null from handle() yields exit code 0 regardless of what was printed. Return self::SUCCESS / self::FAILURE explicitly — CI scripts that chain commands will otherwise pass silently on error.
Extension points: publishable config (if present), the service provider for rebinding, and the package's TestCase hooks (getPackageProviders(), getEnvironmentSetUp(), defineEnvironment()) which are the right place to inject test-specific config rather than mutating the environment globally.
How can I help you explore Laravel packages today?