diff --git a/src/Illuminate/Support/Testing/Fakes/BusFake.php b/src/Illuminate/Support/Testing/Fakes/BusFake.php index 6061344730d8..669f512b3daa 100644 --- a/src/Illuminate/Support/Testing/Fakes/BusFake.php +++ b/src/Illuminate/Support/Testing/Fakes/BusFake.php @@ -71,6 +71,13 @@ class BusFake implements Fake, QueueingDispatcher */ protected $batches = []; + /** + * Indicates if commands should be serialized and restored when pushed to the Bus. + * + * @var bool + */ + protected bool $serializeAndRestore = false; + /** * Create a new bus fake instance. * @@ -585,7 +592,7 @@ public function hasDispatchedAfterResponse($command) public function dispatch($command) { if ($this->shouldFakeJob($command)) { - $this->commands[get_class($command)][] = $command; + $this->commands[get_class($command)][] = $this->getCommandRepresentation($command); } else { return $this->dispatcher->dispatch($command); } @@ -603,7 +610,7 @@ public function dispatch($command) public function dispatchSync($command, $handler = null) { if ($this->shouldFakeJob($command)) { - $this->commandsSync[get_class($command)][] = $command; + $this->commandsSync[get_class($command)][] = $this->getCommandRepresentation($command); } else { return $this->dispatcher->dispatchSync($command, $handler); } @@ -619,7 +626,7 @@ public function dispatchSync($command, $handler = null) public function dispatchNow($command, $handler = null) { if ($this->shouldFakeJob($command)) { - $this->commands[get_class($command)][] = $command; + $this->commands[get_class($command)][] = $this->getCommandRepresentation($command); } else { return $this->dispatcher->dispatchNow($command, $handler); } @@ -634,7 +641,7 @@ public function dispatchNow($command, $handler = null) public function dispatchToQueue($command) { if ($this->shouldFakeJob($command)) { - $this->commands[get_class($command)][] = $command; + $this->commands[get_class($command)][] = $this->getCommandRepresentation($command); } else { return $this->dispatcher->dispatchToQueue($command); } @@ -649,7 +656,7 @@ public function dispatchToQueue($command) public function dispatchAfterResponse($command) { if ($this->shouldFakeJob($command)) { - $this->commandsAfterResponse[get_class($command)][] = $command; + $this->commandsAfterResponse[get_class($command)][] = $this->getCommandRepresentation($command); } else { return $this->dispatcher->dispatch($command); } @@ -754,6 +761,41 @@ protected function shouldDispatchCommand($command) })->isNotEmpty(); } + /** + * Specify if commands should be serialized and restored when being batched. + * + * @param bool $serializeAndRestore + * @return $this + */ + public function serializeAndRestore(bool $serializeAndRestore = true) + { + $this->serializeAndRestore = $serializeAndRestore; + + return $this; + } + + /** + * Serialize and unserialize the command to simulate the queueing process. + * + * @param mixed $command + * @return mixed + */ + protected function serializeAndRestoreCommand($command) + { + return unserialize(serialize($command)); + } + + /** + * Return the command representation that should be stored. + * + * @param mixed $command + * @return mixed + */ + protected function getCommandRepresentation($command) + { + return $this->serializeAndRestore ? $this->serializeAndRestoreCommand($command) : $command; + } + /** * Set the pipes commands should be piped through before dispatching. * diff --git a/src/Illuminate/Support/Testing/Fakes/QueueFake.php b/src/Illuminate/Support/Testing/Fakes/QueueFake.php index 7218644ba860..aa63e2e10c18 100644 --- a/src/Illuminate/Support/Testing/Fakes/QueueFake.php +++ b/src/Illuminate/Support/Testing/Fakes/QueueFake.php @@ -43,6 +43,13 @@ class QueueFake extends QueueManager implements Fake, Queue */ protected $jobs = []; + /** + * Indicates if items should be serialized and restored when pushed to the queue. + * + * @var bool + */ + protected bool $serializeAndRestore = false; + /** * Create a new fake queue instance. * @@ -352,7 +359,7 @@ public function push($job, $data = '', $queue = null) } $this->jobs[is_object($job) ? get_class($job) : $job][] = [ - 'job' => $job, + 'job' => $this->serializeAndRestore ? $this->serializeAndRestoreJob($job) : $job, 'queue' => $queue, 'data' => $data, ]; @@ -491,6 +498,30 @@ public function pushedJobs() return $this->jobs; } + /** + * Specify if jobs should be serialized and restored when being "pushed" to the queue. + * + * @param bool $serializeAndRestore + * @return $this + */ + public function serializeAndRestore(bool $serializeAndRestore = true) + { + $this->serializeAndRestore = $serializeAndRestore; + + return $this; + } + + /** + * Serialize and unserialize the job to simulate the queueing process. + * + * @param mixed $job + * @return mixed + */ + protected function serializeAndRestoreJob($job) + { + return unserialize(serialize($job)); + } + /** * Get the connection name for the queue. * diff --git a/tests/Support/SupportTestingBusFakeTest.php b/tests/Support/SupportTestingBusFakeTest.php index 4ea6cdd9b19d..a95de8c32bc1 100644 --- a/tests/Support/SupportTestingBusFakeTest.php +++ b/tests/Support/SupportTestingBusFakeTest.php @@ -7,7 +7,9 @@ use Illuminate\Contracts\Bus\QueueingDispatcher; use Illuminate\Support\Testing\Fakes\BatchRepositoryFake; use Illuminate\Support\Testing\Fakes\BusFake; +use Illuminate\Support\Testing\Fakes\PendingBatchFake; use Mockery as m; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; @@ -666,6 +668,55 @@ public function testDecrementPendingJobsInFakeBatch() $this->assertSame(0, $batch->failedJobs); $this->assertSame(0, $batch->pendingJobs); } + + #[DataProvider('serializeAndRestoreCommandMethodsDataProvider')] + public function testCanSerializeAndRestoreCommands($commandFunctionName, $assertionFunctionName) + { + $serializingBusFake = (clone $this->fake)->serializeAndRestore(); + + // without setting the serialization, the job should return the value passed in + $this->fake->{$commandFunctionName}(new BusFakeJobWithSerialization('hello')); + $this->fake->{$assertionFunctionName}(BusFakeJobWithSerialization::class, fn($command) => $command->value === 'hello'); + + // when enabling the serializeAndRestore property, job has value modified + $serializingBusFake->{$commandFunctionName}(new BusFakeJobWithSerialization('hello')); + $serializingBusFake->{$assertionFunctionName}( + BusFakeJobWithSerialization::class, + fn($command) => $command->value === 'hello-serialized-unserialized' + ); + } + + public static function serializeAndRestoreCommandMethodsDataProvider(): array + { + return [ + 'dispatch' => ['dispatch', 'assertDispatched'], + 'dispatchSync' => ['dispatchSync', 'assertDispatchedSync'], + 'dispatchNow' => ['dispatchNow', 'assertDispatched'], + 'dispatchAfterResponse' => ['dispatchAfterResponse', 'assertDispatchedAfterResponse'], + ]; + } + + public function testCanSerializeAndRestoreCommandsInBatch() + { + $serializingBusFake = (clone $this->fake)->serializeAndRestore(); + + // without setting the serialization, the batch should return the value passed in + $this->fake->batch([ + new BusFakeJobWithSerialization('hello'), + ])->dispatch(); + $this->fake->assertBatched(function(PendingBatchFake $batchedCollection) { + return $batchedCollection->jobs->count() === 1 && $batchedCollection->jobs->first()->value === 'hello'; + }); + + // when enabling the serializeAndRestore property, each batch jobs will each be serialized/restored + $serializingBusFake->batch([ + new BusFakeJobWithSerialization('hello'), + ])->dispatch(); + + $serializingBusFake->assertBatched(function(PendingBatchFake $batchedCollection) { + return $batchedCollection->jobs->count() === 1 && $batchedCollection->jobs->first()->value === 'hello'; + }); + } } class BusJobStub @@ -692,3 +743,23 @@ class ThirdJob { // } + + +class BusFakeJobWithSerialization +{ + use Queueable; + + public function __construct(public $value) + { + } + + public function __serialize(): array + { + return ['value' => $this->value .'-serialized']; + } + + public function __unserialize(array $data): void + { + $this->value = $data['value'] .'-unserialized'; + } +} diff --git a/tests/Support/SupportTestingQueueFakeTest.php b/tests/Support/SupportTestingQueueFakeTest.php index b1d2afefbf25..468eafdf26da 100644 --- a/tests/Support/SupportTestingQueueFakeTest.php +++ b/tests/Support/SupportTestingQueueFakeTest.php @@ -374,6 +374,24 @@ public function testItDoesntFakeJobsPassedViaExcept() $fake->assertNotPushed(JobStub::class); $fake->assertPushed(JobToFakeStub::class); } + + public function testItCanSerializeAndRestoreJobs() + { + // confirm that the default behavior is maintained + $this->fake->push(new JobWithSerialization('hello')); + $this->fake->assertPushed(JobWithSerialization::class, fn($job) => $job->value === 'hello'); + + $job = new JobWithSerialization('hello'); + + $fake = new QueueFake(new Application); + $fake->serializeAndRestore(); + $fake->push($job); + + $fake->assertPushed( + JobWithSerialization::class, + fn($job) => $job->value === 'hello-serialized-unserialized' + ); + } } class JobStub @@ -424,3 +442,22 @@ public function handle() // } } + +class JobWithSerialization +{ + use Queueable; + + public function __construct(public $value) + { + } + + public function __serialize(): array + { + return ['value' => $this->value .'-serialized']; + } + + public function __unserialize(array $data): void + { + $this->value = $data['value'] .'-unserialized'; + } +}