Testing
Testing a Publisher
Section titled “Testing a Publisher”Call Spoolrail::fake() before exercising code that publishes messages. The fake records the topic, message type, payload, and headers without publishing to a transport, running subscribers, or writing outbox records:
use Spoolrail\Spoolrail\Facades\Spoolrail;use Spoolrail\Spoolrail\Message;
Spoolrail::fake();
Spoolrail::publish( 'orders', Message::make('order.created', ['order_id' => 42]), ['trace-id' => 'trace-42'],);
Spoolrail::assertPublished( 'orders', 'order.created', fn (array $payload, array $headers): bool => $payload['order_id'] === 42 && $headers['trace-id'] === 'trace-42',);Spoolrail::assertPublished('orders', 'order.created', 1);Spoolrail::assertNotPublished('orders', 'order.cancelled');The closure receives the payload and headers. Pass an integer as the third argument to assert the exact publication count. Use Spoolrail::assertNothingPublished() when the action should publish no messages.
Use the fake for producer-side assertions. It does not run transport-portability validation or exercise publication mode, outbox transactions, or delivery guarantees. Use an unfaked array connection when a test depends on portability validation, routing, queue handoff, or handler behavior.
Using the Array Connection
Section titled “Using the Array Connection”The array connection keeps messages in memory for the current PHP process:
config([ 'spoolrail.default' => 'array', 'spoolrail.handoff_idempotency.cache_store' => 'array', 'queue.default' => 'sync',]);This sends messages through the in-memory Spoolrail connection, keeps handoff idempotency state in memory, and runs handlers through Laravel’s synchronous queue connection.
Declare subscriptions before publishing. Publish and consume in the same test process; a separate php artisan process cannot see the in-memory messages.
On the array connection, spoolrail with an explicit subscription returns after it drains that subscription’s buffered messages. All-subscription supervision is unavailable because a clean child process cannot observe messages buffered in the test process.
Testing a Handler
Section titled “Testing a Handler”This Pest test uses the application’s real warehouse-orders subscription:
use App\Services\Inventory;use Spoolrail\Spoolrail\Facades\Spoolrail;use Spoolrail\Spoolrail\Message;
test('reserves inventory for an order message', function (): void { config([ 'spoolrail.default' => 'array', 'spoolrail.handoff_idempotency.cache_store' => 'array', 'queue.default' => 'sync', ]);
$inventory = Mockery::mock(Inventory::class); $inventory->shouldReceive('reserve') ->once() ->with(42);
app()->instance(Inventory::class, $inventory);
Spoolrail::publish( 'orders', Message::make('order.created', [ 'order_id' => 42, ]), );
$this->artisan('spoolrail', [ 'subscription' => 'warehouse-orders', ])->assertSuccessful();});Testing Queued Handling
Section titled “Testing Queued Handling”When a subscription uses an asynchronous queue connection:
- publish through the array Spoolrail connection;
- run
spoolrailwith the subscription name; - run a Laravel queue worker for the selected connection and queue; and
- assert the handler’s domain effect.
Integration Testing with Real Transports
Section titled “Integration Testing with Real Transports”Use transport-backed integration tests when behavior depends on native acceptance, delivery, settlement, or topology. For AWS SNS/SQS tests, point a test connection at a local AWS-compatible endpoint such as MiniStack.
For Google Pub/Sub tests, start the Pub/Sub emulator and expose its standard environment variable to the PHP process:
GOOGLE_CLOUD_PROJECT=spoolrail-testPUBSUB_EMULATOR_HOST=127.0.0.1:8085Leave the Pub/Sub connection’s credentials setting null. Spoolrail follows PUBSUB_EMULATOR_HOST without requiring emulator credentials or a custom endpoint. Use the emulator to test publication, pull delivery, acknowledgment, fanout, and topology. It cannot verify production ordering or exactly-once guarantees.
