diff --git a/src/Illuminate/Bus/Queueable.php b/src/Illuminate/Bus/Queueable.php index eb51ce56d7a1..ae84ca79fc67 100644 --- a/src/Illuminate/Bus/Queueable.php +++ b/src/Illuminate/Bus/Queueable.php @@ -5,6 +5,7 @@ use Closure; use Illuminate\Queue\CallQueuedClosure; use Illuminate\Support\Arr; +use PHPUnit\Framework\Assert as PHPUnit; use RuntimeException; trait Queueable @@ -273,4 +274,39 @@ public function invokeChainCatchCallbacks($e) $callback($e); }); } + + /** + * Assert that the job has the given chain of jobs attached to it. + * + * @param array $expectedChain + * @return void + */ + public function assertHasChain($expectedChain) + { + PHPUnit::assertTrue( + collect($expectedChain)->isNotEmpty(), + 'The expected chain can not be empty.' + ); + + if (collect($expectedChain)->contains(fn ($job) => is_object($job))) { + $expectedChain = collect($expectedChain)->map(fn ($job) => serialize($job))->all(); + } else { + $chain = collect($this->chained)->map(fn ($job) => get_class(unserialize($job)))->all(); + } + + PHPUnit::assertTrue( + $expectedChain === ($chain ?? $this->chained), + 'The job does not have the expected chain.' + ); + } + + /** + * Assert that the job has no remaining chained jobs. + * + * @return void + */ + public function assertDoesntHaveChain() + { + PHPUnit::assertEmpty($this->chained, 'The job has chained jobs.'); + } } diff --git a/tests/Support/SupportTestingQueueFakeTest.php b/tests/Support/SupportTestingQueueFakeTest.php index dd632426d543..3122cbe6fe69 100644 --- a/tests/Support/SupportTestingQueueFakeTest.php +++ b/tests/Support/SupportTestingQueueFakeTest.php @@ -417,6 +417,69 @@ public function testItCanFakePushedJobsWithClassAndPayload() $fake->assertPushed('JobStub', 1); $fake->assertPushed('JobStub', fn ($job, $queue, $payload) => $payload === ['job' => 'payload']); } + + public function testAssertChainUsingClassesOrObjectsArray() + { + $job = new JobWithChainStub([ + new JobStub, + ]); + + $job->assertHasChain([ + JobStub::class, + ]); + + $job->assertHasChain([ + new JobStub(), + ]); + } + + public function testAssertNoChain() + { + $job = new JobWithChainStub([]); + + $job->assertDoesntHaveChain(); + } + + public function testAssertChainErrorHandling() + { + $job = new JobWithChainStub([ + new JobStub, + ]); + + try { + $job->assertHasChain([]); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertStringContainsString('The expected chain can not be empty.', $e->getMessage()); + } + + try { + $job->assertHasChain([ + new JobStub, + new JobStub, + ]); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertStringContainsString('The job does not have the expected chain.', $e->getMessage()); + } + + try { + $job->assertHasChain([ + JobStub::class, + JobStub::class, + ]); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertStringContainsString('The job does not have the expected chain.', $e->getMessage()); + } + + try { + $job->assertDoesntHaveChain(); + $this->fail(); + } catch (ExpectationFailedException $e) { + $this->assertStringContainsString('The job has chained jobs.', $e->getMessage()); + } + } } class JobStub