-
Notifications
You must be signed in to change notification settings - Fork 14
feat: implement queue assertion helpers #287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 39 commits
44f823f
4aa6260
b5c32a3
4e42e4a
a6d3519
ea0f94d
d6073ec
151527e
e6f5df4
85ba290
eab1d55
2df9b17
5a5a790
5c010b9
7a1d4a1
b7e5c3c
a88892f
55bf96b
d8d60be
4a59481
019ae09
59fff99
088de9a
2d18ac1
47f8fb8
a4cc42b
fa52d8a
660fefe
d09cb4a
5aa6369
444ce8f
eb347e7
5ed9bbf
9477cd8
db9e1c4
ad57ad5
6c8111d
335db99
4dd36dc
40aeee9
8189338
41b4fce
4303612
1c9b862
cd5242e
3b53ae1
2e4d0d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,3 +8,5 @@ services: | |
| - 443:443 | ||
| volumes: | ||
| - ./:/app | ||
| extra_hosts: | ||
| - "host.docker.internal:host-gateway" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| <?php | ||
|
|
||
| namespace RonasIT\Support\Traits; | ||
|
|
||
| use ReflectionClass; | ||
|
|
||
| trait ReflectionTrait | ||
| { | ||
| protected function getObjectAttributes(object $object): array | ||
| { | ||
| $reflection = new ReflectionClass($object); | ||
| $attributes = []; | ||
|
|
||
| foreach ($reflection->getProperties() as $property) { | ||
| if ($property->isStatic() || !$property->isInitialized($object)) { | ||
| continue; | ||
| } | ||
|
|
||
| $attributes[$property->getName()] = $property->getValue($object); | ||
| } | ||
|
|
||
| return $attributes; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,15 @@ | |
|
|
||
| namespace RonasIT\Support\Traits; | ||
|
|
||
| use Illuminate\Support\Arr; | ||
| use Illuminate\Support\Facades\Queue; | ||
|
|
||
| trait TestingTrait | ||
| { | ||
| use FixturesTrait; | ||
| use MailsMockTrait; | ||
| use MockTrait; | ||
| use ReflectionTrait; | ||
|
|
||
| protected function assertExceptionThrew(string $expectedClassName, string $expectedMessage, bool $isStrict = true): void | ||
| { | ||
|
|
@@ -18,4 +22,45 @@ protected function assertExceptionThrew(string $expectedClassName, string $expec | |
|
|
||
| $this->expectExceptionMessageMatches("/{$expectedMessage}/"); | ||
| } | ||
|
|
||
| public function assertQueueEqualsVersioningFixture(string $fixture, array $versions = [], bool $exportMode = false): void | ||
| { | ||
| $fixture = $this->getVersioningFixtureName($fixture, $versions); | ||
|
|
||
| $this->assertQueueEqualsFixture($fixture, $exportMode); | ||
| } | ||
|
|
||
| public function assertQueueEqualsFixture(string $fixture, bool $exportMode = false): void | ||
| { | ||
| $actualData = []; | ||
|
|
||
| foreach (Queue::pushedJobs() as $namespace => $jobs) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When code under test calls Useful? React with 👍 / 👎. |
||
| $actualData[$namespace] = Arr::map($jobs, function ($job) { | ||
| $job = $this->getJobObject($job); | ||
|
|
||
| return $this->getObjectAttributes($job); | ||
|
AZabolotnikov marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a test queues a closure job such as Useful? React with 👍 / 👎. |
||
| }); | ||
| } | ||
|
|
||
| $actualData = json_decode(json_encode($actualData), true); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a queued job has an attribute that is itself a DTO/value object with private or protected properties, Useful? React with 👍 / 👎. |
||
|
|
||
| $this->assertEqualsFixture("queue_states/{$fixture}", $actualData, $exportMode); | ||
| } | ||
|
|
||
| protected function assertQueueEmpty(): void | ||
| { | ||
| $this->assertEquals([], Queue::pushedJobs(), 'Failed assert that faked queue is empty.'); | ||
| } | ||
|
|
||
| protected function getJobObject(array $job): object | ||
| { | ||
| if (is_object($job['job'])) { | ||
| return $job['job']; | ||
| } | ||
|
|
||
| $data = Arr::wrap($job['data']); | ||
| $className = $job['job']; | ||
|
|
||
| return new $className(...$data); | ||
|
AZabolotnikov marked this conversation as resolved.
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,13 +2,29 @@ | |
|
|
||
| namespace RonasIT\Support\Tests; | ||
|
|
||
| use Illuminate\Support\Carbon; | ||
| use Illuminate\Support\Facades\Queue; | ||
| use Illuminate\Support\Str; | ||
| use RonasIT\Support\Exceptions\ModelFactoryNotFound; | ||
| use RonasIT\Support\Tests\Support\Mock\Jobs\AnotherTestJob; | ||
| use RonasIT\Support\Tests\Support\Mock\Jobs\TestJob; | ||
| use RonasIT\Support\Traits\TestingTrait; | ||
|
|
||
| class TestingTraitTest extends TestCase | ||
| { | ||
| use TestingTrait; | ||
|
|
||
| public static int $laravelMajorVersion; | ||
|
|
||
| public function setUp(): void | ||
| { | ||
| parent::setUp(); | ||
|
|
||
| Carbon::setTestNow('2020-01-01'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
|
|
||
| self::$laravelMajorVersion ??= (int)Str::before($this->app->version(), '.'); | ||
| } | ||
|
|
||
| public function testAssertExceptionThrew(): void | ||
| { | ||
| $this->assertExceptionThrew(ModelFactoryNotFound::class, 'full error message'); | ||
|
|
@@ -26,4 +42,51 @@ public function testAssertExceptionThrewNotStrictly(): void | |
|
|
||
| throw new ModelFactoryNotFound('full error message'); | ||
| } | ||
|
|
||
| public function testAssertQueueEqualsFixture(): void | ||
| { | ||
| Queue::fake(); | ||
|
|
||
| TestJob::dispatch('some payload', ['another payload'])->delay(now()->addMinute()); | ||
|
|
||
| $this->assertQueueEqualsVersioningFixture('assert_queue_equals', [11, 12]); | ||
| } | ||
|
|
||
| public function testAssertQueueEqualsFixturePushAsStringWithParams(): void | ||
| { | ||
| Queue::fake(); | ||
|
|
||
| Queue::push(TestJob::class, [ | ||
| 'payload' => 'some payload', | ||
| 'anotherPayload' => ['another payload'], | ||
| ]); | ||
|
|
||
| $this->assertQueueEqualsVersioningFixture('assert_queue_equals_as_class_name_with_params', [11, 12]); | ||
| } | ||
|
|
||
| public function testAssertQueueEqualsFixturePushAsStringWithOneParam(): void | ||
| { | ||
| Queue::fake(); | ||
|
|
||
| Queue::push(TestJob::class, 'some payload'); | ||
|
|
||
| $this->assertQueueEqualsVersioningFixture('assert_queue_equals_as_class_name_with_one_param', [11, 12]); | ||
| } | ||
|
|
||
| public function testAssertQueueEqualsFixtureDifferentJobs(): void | ||
| { | ||
| Queue::fake(); | ||
|
|
||
| TestJob::dispatch('some payload', ['another payload']); | ||
| AnotherTestJob::dispatch('some payload', ['another payload']); | ||
|
|
||
| $this->assertQueueEqualsVersioningFixture('assert_queue_equals_different_jobs', [11, 12]); | ||
| } | ||
|
|
||
| public function testAssertQueueEmpty() | ||
| { | ||
| Queue::fake(); | ||
|
|
||
| $this->assertQueueEmpty(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\TestJob": [ | ||
| { | ||
| "payload": "some payload", | ||
| "anotherPayload": [ | ||
| "another payload" | ||
| ], | ||
| "tries": 5, | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": "some_queue", | ||
| "messageGroup": null, | ||
| "deduplicator": null, | ||
| "debounceOwner": "", | ||
|
AZabolotnikov marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fresh evidence beyond the earlier major-version concern is that the new Useful? React with 👍 / 👎. |
||
| "delay": "2020-01-01T00:01:00.000000Z", | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| { | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\TestJob": [ | ||
| { | ||
| "payload": "some payload", | ||
| "anotherPayload": [], | ||
| "tries": 5, | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": "some_queue", | ||
| "messageGroup": null, | ||
| "deduplicator": null, | ||
| "debounceOwner": "", | ||
| "delay": null, | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\TestJob": [ | ||
| { | ||
| "payload": "some payload", | ||
| "anotherPayload": [ | ||
| "another payload" | ||
| ], | ||
| "tries": 5, | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": "some_queue", | ||
| "messageGroup": null, | ||
| "deduplicator": null, | ||
| "debounceOwner": "", | ||
| "delay": null, | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| { | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\TestJob": [ | ||
| { | ||
| "payload": "some payload", | ||
| "anotherPayload": [ | ||
| "another payload" | ||
| ], | ||
| "tries": 5, | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": "some_queue", | ||
| "messageGroup": null, | ||
| "deduplicator": null, | ||
| "debounceOwner": "", | ||
| "delay": null, | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ], | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\AnotherTestJob": [ | ||
| { | ||
| "tries": 5, | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": null, | ||
| "messageGroup": null, | ||
| "deduplicator": null, | ||
| "debounceOwner": "", | ||
| "delay": null, | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| { | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\TestJob": [ | ||
| { | ||
| "tries": 5, | ||
| "payload": "some payload", | ||
| "anotherPayload": [ | ||
| "another payload" | ||
| ], | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": "some_queue", | ||
| "delay": "2020-01-01T00:01:00.000000Z", | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\TestJob": [ | ||
| { | ||
| "tries": 5, | ||
| "payload": "", | ||
| "anotherPayload": [], | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": "some_queue", | ||
| "delay": null, | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "RonasIT\\Support\\Tests\\Support\\Mock\\Jobs\\TestJob": [ | ||
| { | ||
| "tries": 5, | ||
| "payload": "some payload", | ||
| "anotherPayload": [], | ||
| "job": null, | ||
| "connection": null, | ||
| "queue": "some_queue", | ||
| "delay": null, | ||
| "afterCommit": null, | ||
| "middleware": [], | ||
| "chained": [], | ||
| "chainConnection": null, | ||
| "chainQueue": null, | ||
| "chainCatchCallbacks": null | ||
| } | ||
| ] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When jobs of different classes are dispatched to the same queue, iterating
Queue::pushedJobs()snapshots class-keyed buckets instead of the chronological queue sequence, so sequences such asA, B, AandA, A, Bnormalize to the same fixture as long as each class bucket is unchanged. Since the real queue processes jobs in push order, tests using this helper can miss regressions that reorder different job classes; include a flat sequence/index in the captured data rather than only class-keyed buckets.Useful? React with 👍 / 👎.