Skip to content

Comparison with Laravel Native Jobs

Executables are a drop-in replacement for Laravel jobs. Every standard feature is supported. This page confirms full parity and highlights what executables add on top.

Dispatching

Laravel parity

CapabilityLaravel JobExecutable
Dispatch to queueMyJob::dispatch($arg)MyExec::onQueue()->execute($arg)
Dispatch synchronouslyMyJob::dispatchSync($arg)MyExec::sync()->execute($arg)
Dispatch after responseMyJob::dispatchAfterResponse($arg)MyExec::sync()->afterResponse()->execute($arg)
Conditional dispatchMyJob::dispatchIf($cond, $arg)MyExec::onQueue()->when($cond)->execute($arg)
Conditional skipMyJob::dispatchUnless($cond, $arg)MyExec::onQueue()->unless($cond)->execute($arg)
Prepare without dispatchnew MyJob($arg)MyExec::prepare()->execute($arg)

What executables add

CapabilityHow
Multiple conditions->when($a)->when($b)->unless($c)

Closure jobs (dispatch(function () { ... })) are not supported. Executables are class-based by design.

Job Configuration

Laravel parity

All Laravel job configuration works on executables:

FeatureSupported
Max tries ($tries)Yes
Timeout ($timeout)Yes
Backoff ($backoff)Yes
Max exceptionsYes
Fail on timeoutYes
DelayYes
Connection / queueYes
Chain connection / queueYes
After commit / before commitYes
Delete when missing modelsYes
Encryption (ShouldBeEncrypted)Yes
Unique jobs (ShouldBeUnique)Yes
Unique until processingYes
Custom unique cache (uniqueVia())Yes
Display nameYes
Tags (Horizon)Yes
MiddlewareYes
Retry untilYes
Without relationsYes (class-level only, per-argument not supported since execute-arguments aren't constructor properties)
Failed handlerYes

What executables add

FeatureHow
configure() hookDynamic config based on execute-arguments, in a single chainable method
Dispatch-time overrides->withTries(), ->timeout(), ->shouldBeEncrypted(), etc. The caller controls config
->shouldBeUnique() at dispatchEnable uniqueness without implementing the interface
Global without-relations defaultconfig('executable.serialize_models_with_relations')

Job Interaction (inside execute)

Laravel parity

MethodSupported
$this->attempts()Yes
$this->delete()Yes
$this->fail($e)Yes
$this->release($delay)Yes
$this->batch()Yes
$this->prependToChain($job)Yes
$this->appendToChain($job)Yes

Note

The raw underlying job instance is accessible via $this->executableJob->job (equivalent to $this->job on a Laravel job).

Middleware

Laravel parity

All Laravel queue middleware works unchanged: WithoutOverlapping, RateLimited, ThrottlesExceptions, Skip, and any custom middleware.

FeatureSupported
middleware() methodYes
public $middleware propertyYes
Merge property + methodYes

What executables add

FeatureHow
Assert middleware in testsPushedJob::assertHasMiddleware()

Chains

Laravel parity

FeatureSupported
Inline chain on dispatchYes, MyExec::onQueue()->chain([...])->execute()
Bus::chain()Yes, with prepared executables
Chain connection/queueYes
Prepend/append to chainYes

Batches

Laravel parity

Batching delegates entirely to Laravel's Bus::batch(). All batch features work:

FeatureSupported
->then() / ->catch() / ->finally()Yes
->progress()Yes
->allowFailures()Yes
->name() / ->onQueue() / ->onConnection()Yes
$this->batch() inside jobYes
$this->batch()->cancelled()Yes
Chains inside batchesYes

Testing

Laravel parity

CapabilityLaravel JobExecutable
Assert job pushedQueue::assertPushed(MyJob::class)MyExec::assert()->queued()
Assert with callbackQueue::assertPushed(MyJob::class, fn)->where(fn) on queued assertion
Assert countQueue::assertPushed(MyJob::class, 3)->times(3) on queued assertion
Assert on queueQueue::assertPushedOn('q', MyJob::class)->onQueue('q') on queued assertion
Assert not pushedQueue::assertNotPushed(MyJob::class)MyExec::assert()->notQueued()
Assert nothing pushed (global)Queue::assertNothingPushed()Use Queue::assertNothingPushed() directly
Assert pushed with chainQueue::assertPushedWithChain(...)->withChain([...]) on queued assertion
Assert batch dispatchedBus::assertBatched(fn)Execution::assertBatched(fn)
Assert batch countBus::assertBatchCount(n)Execution::assertBatchCount(n)
Assert nothing batchedBus::assertNothingBatched()Execution::assertNothingBatched()

What executables add

These have no equivalent in Laravel's native job testing:

CapabilityHow
Fluent assertion chains->onQueue('q')->with($arg)->once(), no callbacks needed
Assert arguments directly->with($arg1, $arg2) or ->withArgs(fn)
Test release/delete/fail without a queue workerMyExec::test()->execute() + ::assert()->released() / deleted() / failed()
Assert chain built during execution::assert()->hasChain([...]) / hasNoChain()
Mock an executable (replace in container)MyExec::mock()->shouldExecute()->once()->with($arg)
Spy on an executable (real execution + verify)MyExec::spy() + ::assert()->executed()->once()
Inspect individual pushed job propertiesPushedJob wrapper with assertIsOnQueue(), assertIsDelayed(), etc.
Inspect individual batch propertiesPushedBatch wrapper with assertHasName(), assertContains(), etc.
Assert delayed / encrypted / uniquePushedJob::isDelayed(), isEncrypted(), isUnique()
Assert middleware / tagsPushedJob::hasMiddleware(), hasTags()
Assert batch name / queue / callbacksPushedBatch::assertHasName(), assertContainsThenCallback(), etc.
Debug dump pushed jobs::assert()->queued()->dump() / Execution::dumpJobs()
Debug dump batchesExecution::dumpBatches()