From 2b937c4ef80d263b2e2305afdd8d612ab2e22f8e Mon Sep 17 00:00:00 2001 From: pascalbaljet Date: Tue, 13 Sep 2022 09:56:30 +0200 Subject: [PATCH 1/7] WIP --- README.md | 51 ++++++++ composer.json | 3 +- config/dusk-fakes.php | 5 + src/Bus/PersistentBus.php | 20 +++ src/Bus/PersistentBusFake.php | 111 ++++++++++++++++ src/Bus/UncachedPersistentBusFake.php | 41 ++++++ src/LaravelDuskFakesServiceProvider.php | 23 +++- tests/AnotherDummyJob.php | 20 +++ tests/BusTest.php | 162 ++++++++++++++++++++++++ tests/DummyJob.php | 13 ++ tests/MailTest.php | 4 +- tests/NotificationTest.php | 4 +- tests/TestCase.php | 1 + 13 files changed, 452 insertions(+), 6 deletions(-) create mode 100644 src/Bus/PersistentBus.php create mode 100644 src/Bus/PersistentBusFake.php create mode 100644 src/Bus/UncachedPersistentBusFake.php create mode 100644 tests/AnotherDummyJob.php create mode 100644 tests/BusTest.php create mode 100644 tests/DummyJob.php diff --git a/README.md b/README.md index 2f92dea..6b651dd 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,57 @@ You can install the package via composer: composer require protonemedia/laravel-dusk-fakes --dev ``` + +### Persist Bus (queued jobs) + +Make sure you've set the `DUSK_FAKE_BUS` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). + +Finally, add the `PersistentBus` trait to your test. You don't have to manually call the `fake()` method on the `Bus` facade. + +```php +browse(function (Browser $browser) { + $order = Order::factory()->create(); + + $browser->visit('/order/'.$order->id) + ->press('Confirm') + ->waitForText('We will generate an invoice!'); + + Bus::assertDispatched(SendOrderInvoice::class); + }); + } +} +``` + +If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `jobsToFake()` method: + +```php +Bus::jobsToFake(ShipOrder::class); + +$browser->visit(...); + +Bus::assertDispatched(SendOrderInvoice::class); +``` + + ### Persist Mails Make sure you've set the `DUSK_FAKE_MAILS` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). diff --git a/composer.json b/composer.json index ced6339..93944f5 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "require": { "php": "^8.0|^8.1", "illuminate/contracts": "^9.0", - "laravel/dusk": "^7.0" + "laravel/dusk": "^7.0", + "spatie/invade": "^1.1" }, "conflict": { "laravel/framework": "<9.15.0" diff --git a/config/dusk-fakes.php b/config/dusk-fakes.php index e0f0138..704c926 100644 --- a/config/dusk-fakes.php +++ b/config/dusk-fakes.php @@ -1,6 +1,11 @@ [ + 'enabled' => env('DUSK_FAKE_BUS', false), + 'storage_root' => storage_path('framework/testing/bus'), + ], + 'mails' => [ 'enabled' => env('DUSK_FAKE_MAILS', false), 'storage_root' => storage_path('framework/testing/mails'), diff --git a/src/Bus/PersistentBus.php b/src/Bus/PersistentBus.php new file mode 100644 index 0000000..7bed2a6 --- /dev/null +++ b/src/Bus/PersistentBus.php @@ -0,0 +1,20 @@ +directory = rtrim(config('dusk-fakes.bus.storage_root'), '/'); + + $this->storage = $this->directory.'/serialized'; + + (new Filesystem)->ensureDirectoryExists($this->directory); + + $this->loadBus(); + } + + public function jobsToFake($jobsToFake = []) + { + $this->jobsToFake = Arr::wrap($jobsToFake); + + $this->storeBus(); + } + + public function cleanStorage() + { + (new Filesystem)->cleanDirectory($this->directory); + } + + public function loadBus(): self + { + $unserialized = file_exists($this->storage) + ? unserialize(file_get_contents($this->storage)) + : []; + + $this->jobsToFake = $unserialized['jobsToFake'] ?? []; + $this->commands = $unserialized['commands'] ?? []; + $this->commandsSync = $unserialized['commandsSync'] ?? []; + $this->commandsAfterResponse = $unserialized['commandsAfterResponse'] ?? []; + + $this->batches = collect($unserialized['batches'] ?? [])->map(function (PendingBatchFake $batch) { + tap(invade($batch), function ($batch) { + $batch->bus = $this; + }); + + return $batch; + })->all(); + + return $this; + } + + public function dispatch($command) + { + return tap(parent::dispatch($command), fn () => $this->storeBus()); + } + + public function dispatchSync($command, $handler = null) + { + return tap(parent::dispatchSync($command, $handler), fn () => $this->storeBus()); + } + + public function dispatchNow($command, $handler = null) + { + return tap(parent::dispatchNow($command, $handler), fn () => $this->storeBus()); + } + + public function dispatchToQueue($command) + { + return tap(parent::dispatchToQueue($command), fn () => $this->storeBus()); + } + + public function dispatchAfterResponse($command) + { + return tap(parent::dispatchAfterResponse($command), fn () => $this->storeBus()); + } + + public function recordPendingBatch(PendingBatch $pendingBatch) + { + return tap(parent::recordPendingBatch($pendingBatch), fn () => $this->storeBus()); + } + + private function storeBus() + { + file_put_contents($this->storage, serialize([ + 'jobsToFake' => $this->jobsToFake, + 'commands' => $this->commands, + 'commandsSync' => $this->commandsSync, + 'commandsAfterResponse' => $this->commandsAfterResponse, + 'batches' => collect($this->batches)->each(function (PendingBatchFake $batch) { + tap(invade($batch), function ($batch) { + $batch->bus = null; + }); + + return $batch; + })->all(), + ])); + } +} diff --git a/src/Bus/UncachedPersistentBusFake.php b/src/Bus/UncachedPersistentBusFake.php new file mode 100644 index 0000000..3e7eb88 --- /dev/null +++ b/src/Bus/UncachedPersistentBusFake.php @@ -0,0 +1,41 @@ +forwardCallTo($this->fake->loadBus(), $method, $parameters); + } + + /** + * Handle dynamic static method calls into the fake. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + return app(static::class)->$method(...$parameters); + } +} diff --git a/src/LaravelDuskFakesServiceProvider.php b/src/LaravelDuskFakesServiceProvider.php index 38aab95..dbe6fba 100644 --- a/src/LaravelDuskFakesServiceProvider.php +++ b/src/LaravelDuskFakesServiceProvider.php @@ -2,13 +2,17 @@ namespace ProtoneMedia\LaravelDuskFakes; +use Illuminate\Contracts\Bus\QueueingDispatcher; +use Illuminate\Contracts\Support\DeferrableProvider; +use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use Illuminate\Support\ServiceProvider; +use ProtoneMedia\LaravelDuskFakes\Bus\PersistentBusFake; use ProtoneMedia\LaravelDuskFakes\Mails\PersistentMailFake; use ProtoneMedia\LaravelDuskFakes\Notifications\PersistentNotificationFake; -class LaravelDuskFakesServiceProvider extends ServiceProvider +class LaravelDuskFakesServiceProvider extends ServiceProvider implements DeferrableProvider { public function register() { @@ -26,10 +30,27 @@ public function boot() ], 'config'); } + $this->bootFakeBus(); $this->bootFakeMails(); $this->bootFakeNotifications(); } + private function bootFakeBus() + { + if (! config('dusk-fakes.bus.enabled')) { + return; + } + + $fake = new PersistentBusFake(app(QueueingDispatcher::class)); + + $this->app->singleton( + PersistentBusFake::class, + fn () => $fake + ); + + Bus::swap($fake); + } + private function bootFakeMails() { if (! config('dusk-fakes.mails.enabled')) { diff --git a/tests/AnotherDummyJob.php b/tests/AnotherDummyJob.php new file mode 100644 index 0000000..9487d50 --- /dev/null +++ b/tests/AnotherDummyJob.php @@ -0,0 +1,20 @@ +handled = true; + } +} diff --git a/tests/BusTest.php b/tests/BusTest.php new file mode 100644 index 0000000..a0f9e58 --- /dev/null +++ b/tests/BusTest.php @@ -0,0 +1,162 @@ + $dummyTest->tearDownPersistentBus()); + +it('can persist a queued job', function () use ($dummyTest) { + expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); + + Bus::dispatch(new DummyJob); + + expect(storage_path('framework/testing/bus/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentBus(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(UncachedPersistentBusFake::class); + + Bus::assertDispatched(DummyJob::class); + + unlink(storage_path('framework/testing/bus/serialized')); + + Bus::assertNotDispatched(DummyJob::class); +}); + +it('can persist a specific queued job', function () use ($dummyTest) { + expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); + + Bus::jobsToFake(DummyJob::class); + + Bus::dispatch(new AnotherDummyJob); + Bus::dispatch(new DummyJob); + + expect(storage_path('framework/testing/bus/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentBus(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(UncachedPersistentBusFake::class); + + Bus::assertDispatched(DummyJob::class); + Bus::assertNotDispatched(AnotherDummyJob::class); +}); + +it('can persist a queued job using the sync method', function () use ($dummyTest) { + expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); + + Bus::dispatchSync(new DummyJob); + + expect(storage_path('framework/testing/bus/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentBus(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(UncachedPersistentBusFake::class); + + Bus::assertDispatchedSync(DummyJob::class); + + unlink(storage_path('framework/testing/bus/serialized')); + + Bus::assertNotDispatchedSync(DummyJob::class); +}); + +it('can persist a queued job using the now method', function () use ($dummyTest) { + expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); + + Bus::dispatchNow(new DummyJob); + + expect(storage_path('framework/testing/bus/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentBus(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(UncachedPersistentBusFake::class); + + Bus::assertDispatched(DummyJob::class); + + unlink(storage_path('framework/testing/bus/serialized')); + + Bus::assertNotDispatched(DummyJob::class); +}); + +it('can persist a queued job using the toQueue method', function () use ($dummyTest) { + expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); + + Bus::dispatchToQueue(new DummyJob); + + expect(storage_path('framework/testing/bus/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentBus(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(UncachedPersistentBusFake::class); + + Bus::assertDispatched(DummyJob::class); + + unlink(storage_path('framework/testing/bus/serialized')); + + Bus::assertNotDispatched(DummyJob::class); +}); + +it('can persist a queued job using the afterResponse method', function () use ($dummyTest) { + expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); + + dispatch(new DummyJob)->afterResponse(); + + expect(storage_path('framework/testing/bus/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentBus(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(UncachedPersistentBusFake::class); + + Bus::assertDispatchedAfterResponse(DummyJob::class); + + unlink(storage_path('framework/testing/bus/serialized')); + + Bus::assertNotDispatchedAfterResponse(DummyJob::class); +}); + +it('can persist a queued batch', function () use ($dummyTest) { + expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); + + Bus::batch([ + new DummyJob, + new DummyJob, + ])->dispatch(); + + expect(storage_path('framework/testing/bus/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentBus(); + + expect(Bus::getFacadeRoot())->toBeInstanceOf(UncachedPersistentBusFake::class); + + Bus::assertBatchCount(1); + Bus::assertBatched(function (PendingBatchFake $batch) { + return $batch->jobs->count() === 2; + }); + + unlink(storage_path('framework/testing/bus/serialized')); + + Bus::assertBatchCount(0); +}); diff --git a/tests/DummyJob.php b/tests/DummyJob.php new file mode 100644 index 0000000..a581d74 --- /dev/null +++ b/tests/DummyJob.php @@ -0,0 +1,13 @@ + $dummyTest->tearDownPersistentMails()); + it('can persist sent mails', function () use ($dummyTest) { expect(storage_path('framework/testing/mails/serialized'))->not->toBeFile(); @@ -33,8 +35,6 @@ Mail::assertNothingSent(); }); -afterEach(fn () => $dummyTest->tearDownPersistentMails()); - it('can persist queued mails', function () use ($dummyTest) { expect(storage_path('framework/testing/mails/0'))->not->toBeFile(); diff --git a/tests/NotificationTest.php b/tests/NotificationTest.php index be2b39a..15fa9a7 100644 --- a/tests/NotificationTest.php +++ b/tests/NotificationTest.php @@ -12,6 +12,8 @@ use PersistentNotifications; }; +afterEach(fn () => $dummyTest->tearDownPersistentNotifications()); + it('can persist sent notifications', function () use ($dummyTest) { expect(storage_path('framework/testing/notifications/serialized'))->not->toBeFile(); @@ -33,5 +35,3 @@ Notification::assertNothingSent(); }); - -afterEach(fn () => $dummyTest->tearDownPersistentNotifications()); diff --git a/tests/TestCase.php b/tests/TestCase.php index b42bbec..fd20f5c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -16,6 +16,7 @@ protected function getPackageProviders($app) public function getEnvironmentSetUp($app) { + config()->set('dusk-fakes.bus.enabled', true); config()->set('dusk-fakes.mails.enabled', true); config()->set('dusk-fakes.notifications.enabled', true); } From 5a43974d9e88e3bde79a9703b31d666c3106b255 Mon Sep 17 00:00:00 2001 From: pascalbaljet Date: Tue, 13 Sep 2022 10:34:55 +0200 Subject: [PATCH 2/7] WIP --- README.md | 51 +++++++++++++++++- config/dusk-fakes.php | 5 ++ src/Bus/PersistentBusFake.php | 50 +++++++++++------ src/LaravelDuskFakesServiceProvider.php | 20 +++++++ src/Queue/PersistentQueue.php | 20 +++++++ src/Queue/PersistentQueueFake.php | 66 +++++++++++++++++++++++ src/Queue/UncachedPersistentQueueFake.php | 41 ++++++++++++++ tests/AnotherDummyJob.php | 2 - tests/QueueTest.php | 58 ++++++++++++++++++++ tests/TestCase.php | 1 + 10 files changed, 293 insertions(+), 21 deletions(-) create mode 100644 src/Queue/PersistentQueue.php create mode 100644 src/Queue/PersistentQueueFake.php create mode 100644 src/Queue/UncachedPersistentQueueFake.php create mode 100644 tests/QueueTest.php diff --git a/README.md b/README.md index 6b651dd..58bc942 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ You can install the package via composer: composer require protonemedia/laravel-dusk-fakes --dev ``` - ### Persist Bus (queued jobs) Make sure you've set the `DUSK_FAKE_BUS` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). @@ -73,7 +72,6 @@ $browser->visit(...); Bus::assertDispatched(SendOrderInvoice::class); ``` - ### Persist Mails Make sure you've set the `DUSK_FAKE_MAILS` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). @@ -155,6 +153,55 @@ class PasswordResetTest extends DuskTestCase } ``` +### Persist Queue + +Make sure you've set the `DUSK_FAKE_QUEUE` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). + +Finally, add the `PersistentQueue` trait to your test. You don't have to manually call the `fake()` method on the `Queue` facade. + +```php +browse(function (Browser $browser) { + $order = Order::factory()->create(); + + $browser->visit('/order/'.$order->id) + ->press('Confirm') + ->waitForText('We will generate an invoice!'); + + Queue::assertDispatched(SendOrderInvoice::class); + }); + } +} +``` + +If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `jobsToFake()` method: + +```php +Queue::jobsToFake(ShipOrder::class); + +$browser->visit(...); + +Queue::assertDispatched(SendOrderInvoice::class); +``` + ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. diff --git a/config/dusk-fakes.php b/config/dusk-fakes.php index 704c926..e2766f7 100644 --- a/config/dusk-fakes.php +++ b/config/dusk-fakes.php @@ -15,4 +15,9 @@ 'enabled' => env('DUSK_FAKE_NOTIFICATIONS', false), 'storage_root' => storage_path('framework/testing/notifications'), ], + + 'queue' => [ + 'enabled' => env('DUSK_FAKE_QUEUE', false), + 'storage_root' => storage_path('framework/testing/queue'), + ], ]; diff --git a/src/Bus/PersistentBusFake.php b/src/Bus/PersistentBusFake.php index a99fcb1..21d6e8c 100644 --- a/src/Bus/PersistentBusFake.php +++ b/src/Bus/PersistentBusFake.php @@ -21,7 +21,7 @@ public function __construct(QueueingDispatcher $dispatcher, $jobsToFake = []) $this->directory = rtrim(config('dusk-fakes.bus.storage_root'), '/'); - $this->storage = $this->directory.'/serialized'; + $this->storage = $this->directory . '/serialized'; (new Filesystem)->ensureDirectoryExists($this->directory); @@ -46,18 +46,11 @@ public function loadBus(): self ? unserialize(file_get_contents($this->storage)) : []; - $this->jobsToFake = $unserialized['jobsToFake'] ?? []; - $this->commands = $unserialized['commands'] ?? []; - $this->commandsSync = $unserialized['commandsSync'] ?? []; + $this->jobsToFake = $unserialized['jobsToFake'] ?? []; + $this->commands = $unserialized['commands'] ?? []; + $this->commandsSync = $unserialized['commandsSync'] ?? []; $this->commandsAfterResponse = $unserialized['commandsAfterResponse'] ?? []; - - $this->batches = collect($unserialized['batches'] ?? [])->map(function (PendingBatchFake $batch) { - tap(invade($batch), function ($batch) { - $batch->bus = $this; - }); - - return $batch; - })->all(); + $this->batches = $unserialized['batches'] ?? []; return $this; } @@ -92,14 +85,37 @@ public function recordPendingBatch(PendingBatch $pendingBatch) return tap(parent::recordPendingBatch($pendingBatch), fn () => $this->storeBus()); } + public function cleanupCommand(array $jobs): array + { + return collect($jobs)->map(function ($job) { + tap(invade($job), function ($job) { + if (! $job->job) { + return; + } + + $job = invade($job->job); + $job->container = null; + + if (! $job->instance) { + return; + } + + invade($job->instance)->container = null; + invade($job->instance)->dispatcher = null; + }); + + return $job; + })->all(); + } + private function storeBus() { file_put_contents($this->storage, serialize([ - 'jobsToFake' => $this->jobsToFake, - 'commands' => $this->commands, - 'commandsSync' => $this->commandsSync, - 'commandsAfterResponse' => $this->commandsAfterResponse, - 'batches' => collect($this->batches)->each(function (PendingBatchFake $batch) { + 'jobsToFake' => $this->jobsToFake, + 'commands' => collect($this->commands)->map([$this, 'cleanupCommand'])->all(), + 'commandsSync' => collect($this->commandsSync)->map([$this, 'cleanupCommand'])->all(), + 'commandsAfterResponse' => collect($this->commandsAfterResponse)->map([$this, 'cleanupCommand'])->all(), + 'batches' => collect($this->batches)->each(function (PendingBatchFake $batch) { tap(invade($batch), function ($batch) { $batch->bus = null; }); diff --git a/src/LaravelDuskFakesServiceProvider.php b/src/LaravelDuskFakesServiceProvider.php index dbe6fba..ad8846d 100644 --- a/src/LaravelDuskFakesServiceProvider.php +++ b/src/LaravelDuskFakesServiceProvider.php @@ -3,14 +3,17 @@ namespace ProtoneMedia\LaravelDuskFakes; use Illuminate\Contracts\Bus\QueueingDispatcher; +use Illuminate\Contracts\Queue\Queue as QueueContract; use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Facades\Queue; use Illuminate\Support\ServiceProvider; use ProtoneMedia\LaravelDuskFakes\Bus\PersistentBusFake; use ProtoneMedia\LaravelDuskFakes\Mails\PersistentMailFake; use ProtoneMedia\LaravelDuskFakes\Notifications\PersistentNotificationFake; +use ProtoneMedia\LaravelDuskFakes\Queue\PersistentQueueFake; class LaravelDuskFakesServiceProvider extends ServiceProvider implements DeferrableProvider { @@ -33,6 +36,7 @@ public function boot() $this->bootFakeBus(); $this->bootFakeMails(); $this->bootFakeNotifications(); + $this->bootFakeQueue(); } private function bootFakeBus() @@ -82,4 +86,20 @@ private function bootFakeNotifications() Notification::swap($fake); } + + private function bootFakeQueue() + { + if (! config('dusk-fakes.queue.enabled')) { + return; + } + + $fake = new PersistentQueueFake(app(), [], app(QueueContract::class)); + + $this->app->singleton( + PersistentQueueFake::class, + fn () => $fake + ); + + Queue::swap($fake); + } } diff --git a/src/Queue/PersistentQueue.php b/src/Queue/PersistentQueue.php new file mode 100644 index 0000000..fcd9f70 --- /dev/null +++ b/src/Queue/PersistentQueue.php @@ -0,0 +1,20 @@ +directory = rtrim(config('dusk-fakes.queue.storage_root'), '/'); + + $this->storage = $this->directory.'/serialized'; + + (new Filesystem)->ensureDirectoryExists($this->directory); + + $this->loadQueue(); + } + + public function jobsToFake($jobsToFake = []) + { + $this->jobsToFake = Collection::wrap($jobsToFake); + + $this->storeQueue(); + } + + public function cleanStorage() + { + (new Filesystem)->cleanDirectory($this->directory); + } + + public function loadQueue(): self + { + $unserialized = file_exists($this->storage) + ? unserialize(file_get_contents($this->storage)) + : []; + + $this->jobsToFake = Collection::wrap($unserialized['jobsToFake'] ?? []); + $this->jobs = $unserialized['jobs'] ?? []; + + return $this; + } + + public function push($job, $data = '', $queue = null) + { + parent::push($job, $data, $queue); + + $this->storeQueue(); + } + + private function storeQueue() + { + file_put_contents($this->storage, serialize([ + 'jobsToFake' => Collection::wrap($this->jobsToFake)->all(), + 'jobs' => $this->jobs, + ])); + } +} diff --git a/src/Queue/UncachedPersistentQueueFake.php b/src/Queue/UncachedPersistentQueueFake.php new file mode 100644 index 0000000..68082b3 --- /dev/null +++ b/src/Queue/UncachedPersistentQueueFake.php @@ -0,0 +1,41 @@ +forwardCallTo($this->fake->loadQueue(), $method, $parameters); + } + + /** + * Handle dynamic static method calls into the fake. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + return app(static::class)->$method(...$parameters); + } +} diff --git a/tests/AnotherDummyJob.php b/tests/AnotherDummyJob.php index 9487d50..8091da0 100644 --- a/tests/AnotherDummyJob.php +++ b/tests/AnotherDummyJob.php @@ -11,8 +11,6 @@ class AnotherDummyJob implements ShouldQueue { use InteractsWithQueue, Queueable, Batchable; - public bool $handled = false; - public function handle() { $this->handled = true; diff --git a/tests/QueueTest.php b/tests/QueueTest.php new file mode 100644 index 0000000..833c820 --- /dev/null +++ b/tests/QueueTest.php @@ -0,0 +1,58 @@ + $dummyTest->tearDownPersistentQueue()); +afterEach(fn () => $dummyTest->tearDownPersistentQueue()); + +it('can persist a queued job', function () use ($dummyTest) { + expect(storage_path('framework/testing/queue/serialized'))->not->toBeFile(); + + expect(Queue::getFacadeRoot())->toBeInstanceOf(PersistentQueueFake::class); + + Queue::push(new DummyJob); + + expect(storage_path('framework/testing/queue/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentQueue(); + + expect(Queue::getFacadeRoot())->toBeInstanceOf(UncachedPersistentQueueFake::class); + + Queue::assertPushed(DummyJob::class); + + unlink(storage_path('framework/testing/queue/serialized')); + + Queue::assertNotPushed(DummyJob::class); +}); + +it('can persist a specific queued job', function () use ($dummyTest) { + expect(storage_path('framework/testing/queue/serialized'))->not->toBeFile(); + + expect(Queue::getFacadeRoot())->toBeInstanceOf(PersistentQueueFake::class); + + Queue::jobsToFake(DummyJob::class); + + Queue::push(new AnotherDummyJob); + Queue::push(new DummyJob); + + expect(storage_path('framework/testing/queue/serialized'))->toBeFile(); + + $dummyTest->setUpPersistentQueue(); + + expect(Queue::getFacadeRoot())->toBeInstanceOf(UncachedPersistentQueueFake::class); + + Queue::assertPushed(DummyJob::class); + Queue::assertNotPushed(AnotherDummyJob::class); + + unlink(storage_path('framework/testing/queue/serialized')); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php index fd20f5c..46e8b1a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -19,5 +19,6 @@ public function getEnvironmentSetUp($app) config()->set('dusk-fakes.bus.enabled', true); config()->set('dusk-fakes.mails.enabled', true); config()->set('dusk-fakes.notifications.enabled', true); + config()->set('dusk-fakes.queue.enabled', true); } } From 8391d84f79659cd033aa056a8e9e53d1f89f40b2 Mon Sep 17 00:00:00 2001 From: pascalbaljet Date: Tue, 13 Sep 2022 08:35:21 +0000 Subject: [PATCH 3/7] Fix styling --- src/Bus/PersistentBusFake.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Bus/PersistentBusFake.php b/src/Bus/PersistentBusFake.php index 21d6e8c..3a176f8 100644 --- a/src/Bus/PersistentBusFake.php +++ b/src/Bus/PersistentBusFake.php @@ -21,7 +21,7 @@ public function __construct(QueueingDispatcher $dispatcher, $jobsToFake = []) $this->directory = rtrim(config('dusk-fakes.bus.storage_root'), '/'); - $this->storage = $this->directory . '/serialized'; + $this->storage = $this->directory.'/serialized'; (new Filesystem)->ensureDirectoryExists($this->directory); @@ -46,11 +46,11 @@ public function loadBus(): self ? unserialize(file_get_contents($this->storage)) : []; - $this->jobsToFake = $unserialized['jobsToFake'] ?? []; - $this->commands = $unserialized['commands'] ?? []; - $this->commandsSync = $unserialized['commandsSync'] ?? []; + $this->jobsToFake = $unserialized['jobsToFake'] ?? []; + $this->commands = $unserialized['commands'] ?? []; + $this->commandsSync = $unserialized['commandsSync'] ?? []; $this->commandsAfterResponse = $unserialized['commandsAfterResponse'] ?? []; - $this->batches = $unserialized['batches'] ?? []; + $this->batches = $unserialized['batches'] ?? []; return $this; } @@ -93,14 +93,14 @@ public function cleanupCommand(array $jobs): array return; } - $job = invade($job->job); + $job = invade($job->job); $job->container = null; if (! $job->instance) { return; } - invade($job->instance)->container = null; + invade($job->instance)->container = null; invade($job->instance)->dispatcher = null; }); @@ -111,11 +111,11 @@ public function cleanupCommand(array $jobs): array private function storeBus() { file_put_contents($this->storage, serialize([ - 'jobsToFake' => $this->jobsToFake, - 'commands' => collect($this->commands)->map([$this, 'cleanupCommand'])->all(), - 'commandsSync' => collect($this->commandsSync)->map([$this, 'cleanupCommand'])->all(), + 'jobsToFake' => $this->jobsToFake, + 'commands' => collect($this->commands)->map([$this, 'cleanupCommand'])->all(), + 'commandsSync' => collect($this->commandsSync)->map([$this, 'cleanupCommand'])->all(), 'commandsAfterResponse' => collect($this->commandsAfterResponse)->map([$this, 'cleanupCommand'])->all(), - 'batches' => collect($this->batches)->each(function (PendingBatchFake $batch) { + 'batches' => collect($this->batches)->each(function (PendingBatchFake $batch) { tap(invade($batch), function ($batch) { $batch->bus = null; }); From c187a7ed4689a8b5ce0443f392cd2c80e68f2c4d Mon Sep 17 00:00:00 2001 From: pascalbaljet Date: Tue, 13 Sep 2022 10:36:53 +0200 Subject: [PATCH 4/7] Update LaravelDuskFakesServiceProvider.php --- src/LaravelDuskFakesServiceProvider.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/LaravelDuskFakesServiceProvider.php b/src/LaravelDuskFakesServiceProvider.php index ad8846d..cb64b1c 100644 --- a/src/LaravelDuskFakesServiceProvider.php +++ b/src/LaravelDuskFakesServiceProvider.php @@ -4,7 +4,6 @@ use Illuminate\Contracts\Bus\QueueingDispatcher; use Illuminate\Contracts\Queue\Queue as QueueContract; -use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; @@ -15,7 +14,7 @@ use ProtoneMedia\LaravelDuskFakes\Notifications\PersistentNotificationFake; use ProtoneMedia\LaravelDuskFakes\Queue\PersistentQueueFake; -class LaravelDuskFakesServiceProvider extends ServiceProvider implements DeferrableProvider +class LaravelDuskFakesServiceProvider extends ServiceProvider { public function register() { From b46f263d1a2a0b2c98259efd5b37e4b3232815e5 Mon Sep 17 00:00:00 2001 From: pascalbaljet Date: Tue, 13 Sep 2022 10:41:00 +0200 Subject: [PATCH 5/7] WIP --- tests/BusTest.php | 14 -------------- tests/MailTest.php | 4 ---- tests/NotificationTest.php | 2 -- tests/QueueTest.php | 4 ---- 4 files changed, 24 deletions(-) diff --git a/tests/BusTest.php b/tests/BusTest.php index a0f9e58..be21b08 100644 --- a/tests/BusTest.php +++ b/tests/BusTest.php @@ -16,8 +16,6 @@ afterEach(fn () => $dummyTest->tearDownPersistentBus()); it('can persist a queued job', function () use ($dummyTest) { - expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); - expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); Bus::dispatch(new DummyJob); @@ -36,8 +34,6 @@ }); it('can persist a specific queued job', function () use ($dummyTest) { - expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); - expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); Bus::jobsToFake(DummyJob::class); @@ -56,8 +52,6 @@ }); it('can persist a queued job using the sync method', function () use ($dummyTest) { - expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); - expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); Bus::dispatchSync(new DummyJob); @@ -76,8 +70,6 @@ }); it('can persist a queued job using the now method', function () use ($dummyTest) { - expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); - expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); Bus::dispatchNow(new DummyJob); @@ -96,8 +88,6 @@ }); it('can persist a queued job using the toQueue method', function () use ($dummyTest) { - expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); - expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); Bus::dispatchToQueue(new DummyJob); @@ -116,8 +106,6 @@ }); it('can persist a queued job using the afterResponse method', function () use ($dummyTest) { - expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); - expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); dispatch(new DummyJob)->afterResponse(); @@ -136,8 +124,6 @@ }); it('can persist a queued batch', function () use ($dummyTest) { - expect(storage_path('framework/testing/bus/serialized'))->not->toBeFile(); - expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); Bus::batch([ diff --git a/tests/MailTest.php b/tests/MailTest.php index 4871a0e..0e689af 100644 --- a/tests/MailTest.php +++ b/tests/MailTest.php @@ -14,8 +14,6 @@ afterEach(fn () => $dummyTest->tearDownPersistentMails()); it('can persist sent mails', function () use ($dummyTest) { - expect(storage_path('framework/testing/mails/serialized'))->not->toBeFile(); - expect(Mail::getFacadeRoot())->toBeInstanceOf(PersistentMailFake::class); Mail::to('test@example.com')->send(new DummyMail); @@ -36,8 +34,6 @@ }); it('can persist queued mails', function () use ($dummyTest) { - expect(storage_path('framework/testing/mails/0'))->not->toBeFile(); - expect(Mail::getFacadeRoot())->toBeInstanceOf(PersistentMailFake::class); Mail::to('test@example.com')->queue(new DummyMail); diff --git a/tests/NotificationTest.php b/tests/NotificationTest.php index 15fa9a7..23dfc61 100644 --- a/tests/NotificationTest.php +++ b/tests/NotificationTest.php @@ -15,8 +15,6 @@ afterEach(fn () => $dummyTest->tearDownPersistentNotifications()); it('can persist sent notifications', function () use ($dummyTest) { - expect(storage_path('framework/testing/notifications/serialized'))->not->toBeFile(); - expect(Notification::getFacadeRoot())->toBeInstanceOf(PersistentNotificationFake::class); $user = (new DummyUser)->forceFill(['id' => 1]); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index 833c820..c251888 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -16,8 +16,6 @@ afterEach(fn () => $dummyTest->tearDownPersistentQueue()); it('can persist a queued job', function () use ($dummyTest) { - expect(storage_path('framework/testing/queue/serialized'))->not->toBeFile(); - expect(Queue::getFacadeRoot())->toBeInstanceOf(PersistentQueueFake::class); Queue::push(new DummyJob); @@ -36,8 +34,6 @@ }); it('can persist a specific queued job', function () use ($dummyTest) { - expect(storage_path('framework/testing/queue/serialized'))->not->toBeFile(); - expect(Queue::getFacadeRoot())->toBeInstanceOf(PersistentQueueFake::class); Queue::jobsToFake(DummyJob::class); From 952556ee72532636192d143294a1c181da604b76 Mon Sep 17 00:00:00 2001 From: pascalbaljet Date: Mon, 3 Apr 2023 12:35:59 +0200 Subject: [PATCH 6/7] Fixes --- .github/workflows/run-tests.yml | 9 ++------- composer.json | 4 ++-- src/Bus/PersistentBusFake.php | 2 +- src/Queue/PersistentQueueFake.php | 2 +- tests/BusTest.php | 3 ++- tests/MailTest.php | 3 ++- tests/NotificationTest.php | 3 ++- tests/QueueTest.php | 4 ++-- 8 files changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 050ac21..4ed3a5c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -12,18 +12,13 @@ jobs: strategy: fail-fast: true matrix: - php: [8.2, 8.1, 8.0] - laravel: [10.*, 9.*] + php: [8.2, 8.1] + laravel: [10.*] os: [ubuntu-latest, windows-latest] stability: [prefer-lowest, prefer-stable] include: - laravel: 10.* testbench: 8.* - - laravel: 9.* - testbench: 7.* - exclude: - - laravel: 10.* - php: 8.0 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} diff --git a/composer.json b/composer.json index 8b493ff..74dc192 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^8.0|^8.1|^8.2", + "php": "^8.1|^8.2", "illuminate/contracts": "^10.0", "laravel/dusk": "^7.0", "spatie/invade": "^1.1" @@ -28,7 +28,7 @@ "orchestra/testbench": "^8.0", "pestphp/pest": "^1.21", "pestphp/pest-plugin-laravel": "^1.1", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^9.5|^10.0" }, "autoload": { "psr-4": { diff --git a/src/Bus/PersistentBusFake.php b/src/Bus/PersistentBusFake.php index 3a176f8..caf5ab7 100644 --- a/src/Bus/PersistentBusFake.php +++ b/src/Bus/PersistentBusFake.php @@ -43,7 +43,7 @@ public function cleanStorage() public function loadBus(): self { $unserialized = file_exists($this->storage) - ? unserialize(file_get_contents($this->storage)) + ? rescue(fn () => unserialize(file_get_contents($this->storage)), [], false) : []; $this->jobsToFake = $unserialized['jobsToFake'] ?? []; diff --git a/src/Queue/PersistentQueueFake.php b/src/Queue/PersistentQueueFake.php index b9addfa..dd95c34 100644 --- a/src/Queue/PersistentQueueFake.php +++ b/src/Queue/PersistentQueueFake.php @@ -40,7 +40,7 @@ public function cleanStorage() public function loadQueue(): self { $unserialized = file_exists($this->storage) - ? unserialize(file_get_contents($this->storage)) + ? rescue(fn () => unserialize(file_get_contents($this->storage)), [], false) : []; $this->jobsToFake = Collection::wrap($unserialized['jobsToFake'] ?? []); diff --git a/tests/BusTest.php b/tests/BusTest.php index be21b08..dc4d8c6 100644 --- a/tests/BusTest.php +++ b/tests/BusTest.php @@ -13,7 +13,8 @@ use PersistentBus; }; -afterEach(fn () => $dummyTest->tearDownPersistentBus()); +beforeEach(fn () => app(PersistentBusFake::class)->cleanStorage()); +afterEach(fn () => app(PersistentBusFake::class)->cleanStorage()); it('can persist a queued job', function () use ($dummyTest) { expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); diff --git a/tests/MailTest.php b/tests/MailTest.php index 0e689af..4cd46ed 100644 --- a/tests/MailTest.php +++ b/tests/MailTest.php @@ -11,7 +11,8 @@ use PersistentMails; }; -afterEach(fn () => $dummyTest->tearDownPersistentMails()); +beforeEach(fn () => app(PersistentMailFake::class)->cleanStorage()); +afterEach(fn () => app(PersistentMailFake::class)->cleanStorage()); it('can persist sent mails', function () use ($dummyTest) { expect(Mail::getFacadeRoot())->toBeInstanceOf(PersistentMailFake::class); diff --git a/tests/NotificationTest.php b/tests/NotificationTest.php index 23dfc61..012b4d3 100644 --- a/tests/NotificationTest.php +++ b/tests/NotificationTest.php @@ -12,7 +12,8 @@ use PersistentNotifications; }; -afterEach(fn () => $dummyTest->tearDownPersistentNotifications()); +beforeEach(fn () => app(PersistentNotificationFake::class)->cleanStorage()); +afterEach(fn () => app(PersistentNotificationFake::class)->cleanStorage()); it('can persist sent notifications', function () use ($dummyTest) { expect(Notification::getFacadeRoot())->toBeInstanceOf(PersistentNotificationFake::class); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index c251888..cc77b6e 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -12,8 +12,8 @@ use PersistentQueue; }; -beforeEach(fn () => $dummyTest->tearDownPersistentQueue()); -afterEach(fn () => $dummyTest->tearDownPersistentQueue()); +beforeEach(fn () => app(PersistentQueueFake::class)->cleanStorage()); +afterEach(fn () => app(PersistentQueueFake::class)->cleanStorage()); it('can persist a queued job', function () use ($dummyTest) { expect(Queue::getFacadeRoot())->toBeInstanceOf(PersistentQueueFake::class); From 1982ce405833925721231e56bb5712d50d43c214 Mon Sep 17 00:00:00 2001 From: Pascal Baljet Date: Tue, 4 Apr 2023 11:47:07 +0200 Subject: [PATCH 7/7] Fixes --- src/Bus/PersistentBusFake.php | 2 ++ src/Mails/PersistentMailFake.php | 2 ++ src/Notifications/PersistentNotificationFake.php | 2 ++ src/Queue/PersistentQueueFake.php | 8 ++++++-- tests/BusTest.php | 4 ++-- tests/MailTest.php | 4 ++-- tests/NotificationTest.php | 4 ++-- tests/QueueTest.php | 4 ++-- 8 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Bus/PersistentBusFake.php b/src/Bus/PersistentBusFake.php index caf5ab7..b974fc7 100644 --- a/src/Bus/PersistentBusFake.php +++ b/src/Bus/PersistentBusFake.php @@ -110,6 +110,8 @@ public function cleanupCommand(array $jobs): array private function storeBus() { + (new Filesystem)->ensureDirectoryExists($this->directory); + file_put_contents($this->storage, serialize([ 'jobsToFake' => $this->jobsToFake, 'commands' => collect($this->commands)->map([$this, 'cleanupCommand'])->all(), diff --git a/src/Mails/PersistentMailFake.php b/src/Mails/PersistentMailFake.php index 400b5f8..95ec819 100644 --- a/src/Mails/PersistentMailFake.php +++ b/src/Mails/PersistentMailFake.php @@ -55,6 +55,8 @@ public function queue($view, $queue = null) private function storeMails() { + (new Filesystem)->ensureDirectoryExists($this->directory); + file_put_contents($this->storage, serialize([ 'mailables' => $this->mailables, 'queuedMailables' => $this->queuedMailables, diff --git a/src/Notifications/PersistentNotificationFake.php b/src/Notifications/PersistentNotificationFake.php index da26c6f..ab73490 100644 --- a/src/Notifications/PersistentNotificationFake.php +++ b/src/Notifications/PersistentNotificationFake.php @@ -45,6 +45,8 @@ public function sendNow($notifiables, $notification, array $channels = null) private function storeNotifications() { + (new Filesystem)->ensureDirectoryExists($this->directory); + file_put_contents($this->storage, serialize($this->notifications)); } } diff --git a/src/Queue/PersistentQueueFake.php b/src/Queue/PersistentQueueFake.php index dd95c34..58c8ad1 100644 --- a/src/Queue/PersistentQueueFake.php +++ b/src/Queue/PersistentQueueFake.php @@ -43,7 +43,8 @@ public function loadQueue(): self ? rescue(fn () => unserialize(file_get_contents($this->storage)), [], false) : []; - $this->jobsToFake = Collection::wrap($unserialized['jobsToFake'] ?? []); + $this->jobsToFake = Collection::make($unserialized['jobsToFake'] ?? []); + $this->jobsToBeQueued = Collection::make($unserialized['jobsToBeQueued'] ?? []); $this->jobs = $unserialized['jobs'] ?? []; return $this; @@ -58,8 +59,11 @@ public function push($job, $data = '', $queue = null) private function storeQueue() { + (new Filesystem)->ensureDirectoryExists($this->directory); + file_put_contents($this->storage, serialize([ - 'jobsToFake' => Collection::wrap($this->jobsToFake)->all(), + 'jobsToFake' => $this->jobsToFake->all(), + 'jobsToBeQueued' => $this->jobsToBeQueued->all(), 'jobs' => $this->jobs, ])); } diff --git a/tests/BusTest.php b/tests/BusTest.php index dc4d8c6..6b92a05 100644 --- a/tests/BusTest.php +++ b/tests/BusTest.php @@ -1,5 +1,6 @@ app(PersistentBusFake::class)->cleanStorage()); -afterEach(fn () => app(PersistentBusFake::class)->cleanStorage()); +afterEach(fn () => (new Filesystem)->cleanDirectory(storage_path('framework/testing'))); it('can persist a queued job', function () use ($dummyTest) { expect(Bus::getFacadeRoot())->toBeInstanceOf(PersistentBusFake::class); diff --git a/tests/MailTest.php b/tests/MailTest.php index 4cd46ed..26b4c3d 100644 --- a/tests/MailTest.php +++ b/tests/MailTest.php @@ -1,5 +1,6 @@ app(PersistentMailFake::class)->cleanStorage()); -afterEach(fn () => app(PersistentMailFake::class)->cleanStorage()); +afterEach(fn () => (new Filesystem)->cleanDirectory(storage_path('framework/testing'))); it('can persist sent mails', function () use ($dummyTest) { expect(Mail::getFacadeRoot())->toBeInstanceOf(PersistentMailFake::class); diff --git a/tests/NotificationTest.php b/tests/NotificationTest.php index 012b4d3..a887501 100644 --- a/tests/NotificationTest.php +++ b/tests/NotificationTest.php @@ -1,5 +1,6 @@ app(PersistentNotificationFake::class)->cleanStorage()); -afterEach(fn () => app(PersistentNotificationFake::class)->cleanStorage()); +afterEach(fn () => (new Filesystem)->cleanDirectory(storage_path('framework/testing'))); it('can persist sent notifications', function () use ($dummyTest) { expect(Notification::getFacadeRoot())->toBeInstanceOf(PersistentNotificationFake::class); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index cc77b6e..b74a41d 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -1,5 +1,6 @@ app(PersistentQueueFake::class)->cleanStorage()); -afterEach(fn () => app(PersistentQueueFake::class)->cleanStorage()); +afterEach(fn () => (new Filesystem)->cleanDirectory(storage_path('framework/testing'))); it('can persist a queued job', function () use ($dummyTest) { expect(Queue::getFacadeRoot())->toBeInstanceOf(PersistentQueueFake::class);