From 5deab59e89b85e09b2bd1642e4efe55e933805ca Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 Sep 2016 20:36:00 -0500 Subject: [PATCH] add fakes for bus, events, mail, queue, notifications --- src/Illuminate/Support/Facades/Bus.php | 12 + src/Illuminate/Support/Facades/Event.php | 12 + src/Illuminate/Support/Facades/Mail.php | 12 + .../Support/Facades/Notification.php | 12 + src/Illuminate/Support/Facades/Queue.php | 12 + .../Support/Testing/Fakes/BusFake.php | 117 ++++++++++ .../Support/Testing/Fakes/EventFake.php | 216 ++++++++++++++++++ .../Support/Testing/Fakes/MailFake.php | 216 ++++++++++++++++++ .../Support/Testing/Fakes/MailableFake.php | 70 ++++++ .../Testing/Fakes/NotificationFake.php | 153 +++++++++++++ .../Support/Testing/Fakes/QueueFake.php | 198 ++++++++++++++++ 11 files changed, 1030 insertions(+) create mode 100644 src/Illuminate/Support/Testing/Fakes/BusFake.php create mode 100644 src/Illuminate/Support/Testing/Fakes/EventFake.php create mode 100644 src/Illuminate/Support/Testing/Fakes/MailFake.php create mode 100644 src/Illuminate/Support/Testing/Fakes/MailableFake.php create mode 100644 src/Illuminate/Support/Testing/Fakes/NotificationFake.php create mode 100644 src/Illuminate/Support/Testing/Fakes/QueueFake.php diff --git a/src/Illuminate/Support/Facades/Bus.php b/src/Illuminate/Support/Facades/Bus.php index 7c42e9ed0ccf..44271b9c9652 100644 --- a/src/Illuminate/Support/Facades/Bus.php +++ b/src/Illuminate/Support/Facades/Bus.php @@ -2,11 +2,23 @@ namespace Illuminate\Support\Facades; +use Illuminate\Support\Testing\Fakes\BusFake; + /** * @see \Illuminate\Contracts\Bus\Dispatcher */ class Bus extends Facade { + /** + * Replace the bound instance with a fake. + * + * @return void + */ + public static function fake() + { + static::swap(new BusFake); + } + /** * Get the registered name of the component. * diff --git a/src/Illuminate/Support/Facades/Event.php b/src/Illuminate/Support/Facades/Event.php index f08306923ab6..cbdc668ac0ae 100755 --- a/src/Illuminate/Support/Facades/Event.php +++ b/src/Illuminate/Support/Facades/Event.php @@ -2,11 +2,23 @@ namespace Illuminate\Support\Facades; +use Illuminate\Support\Testing\Fakes\EventFake; + /** * @see \Illuminate\Events\Dispatcher */ class Event extends Facade { + /** + * Replace the bound instance with a fake. + * + * @return void + */ + public static function fake() + { + static::swap(new EventFake); + } + /** * Get the registered name of the component. * diff --git a/src/Illuminate/Support/Facades/Mail.php b/src/Illuminate/Support/Facades/Mail.php index 28ea9abc8130..f5140c476f24 100755 --- a/src/Illuminate/Support/Facades/Mail.php +++ b/src/Illuminate/Support/Facades/Mail.php @@ -2,11 +2,23 @@ namespace Illuminate\Support\Facades; +use Illuminate\Support\Testing\Fakes\MailFake; + /** * @see \Illuminate\Mail\Mailer */ class Mail extends Facade { + /** + * Replace the bound instance with a fake. + * + * @return void + */ + public static function fake() + { + static::swap(new MailFake); + } + /** * Get the registered name of the component. * diff --git a/src/Illuminate/Support/Facades/Notification.php b/src/Illuminate/Support/Facades/Notification.php index 58ee7cab86d9..03917320c323 100644 --- a/src/Illuminate/Support/Facades/Notification.php +++ b/src/Illuminate/Support/Facades/Notification.php @@ -2,11 +2,23 @@ namespace Illuminate\Support\Facades; +use Illuminate\Support\Testing\Fakes\NotificationFake; + /** * @see \Illuminate\Notifications\ChannelManager */ class Notification extends Facade { + /** + * Replace the bound instance with a fake. + * + * @return void + */ + public static function fake() + { + static::swap(new NotificationFake); + } + /** * Get the registered name of the component. * diff --git a/src/Illuminate/Support/Facades/Queue.php b/src/Illuminate/Support/Facades/Queue.php index 4a9a3eb348e5..9de3293e308e 100755 --- a/src/Illuminate/Support/Facades/Queue.php +++ b/src/Illuminate/Support/Facades/Queue.php @@ -2,12 +2,24 @@ namespace Illuminate\Support\Facades; +use Illuminate\Support\Testing\Fakes\QueueFake; + /** * @see \Illuminate\Queue\QueueManager * @see \Illuminate\Queue\Queue */ class Queue extends Facade { + /** + * Replace the bound instance with a fake. + * + * @return void + */ + public static function fake() + { + static::swap(new QueueFake); + } + /** * Get the registered name of the component. * diff --git a/src/Illuminate/Support/Testing/Fakes/BusFake.php b/src/Illuminate/Support/Testing/Fakes/BusFake.php new file mode 100644 index 000000000000..62d28ce1e4ee --- /dev/null +++ b/src/Illuminate/Support/Testing/Fakes/BusFake.php @@ -0,0 +1,117 @@ +dispatched($command, $callback)->count() > 0, + "The expected [{$command}] job was not dispatched." + ); + } + + /** + * Determine if a job was dispatched based on a truth-test callback. + * + * @param string $command + * @param callable|null $callback + * @return void + */ + public function assertNotDispatched($command, $callback = null) + { + PHPUnit::assertTrue( + $this->dispatched($command, $callback)->count() === 0, + "The unexpected [{$command}] job was dispatched." + ); + } + + /** + * Get all of the jobs matching a truth-test callback. + * + * @param string $command + * @param callable|null $callback + * @return \Illuminate\Support\Collection + */ + public function dispatched($command, $callback = null) + { + if (! $this->hasDispatched($command)) { + return collect(); + } + + $callback = $callback ?: function () { + return true; + }; + + if (is_null($callback)) { + return collect($this->commands[$command]); + } + + return collect($this->commands[$command])->filter(function ($command) use ($callback) { + return $callback($command); + }); + } + + /** + * Determine if there are any stored commands for a given class. + * + * @param string $command + * @return bool + */ + public function hasDispatched($command) + { + return isset($this->commands[$command]) && ! empty($this->commands[$command]); + } + + /** + * Dispatch a command to its appropriate handler. + * + * @param mixed $command + * @return mixed + */ + public function dispatch($command) + { + return $this->dispatchNow($command); + } + + /** + * Dispatch a command to its appropriate handler in the current process. + * + * @param mixed $command + * @param mixed $handler + * @return mixed + */ + public function dispatchNow($command, $handler = null) + { + $this->commands[get_class($command)][] = $command; + } + + /** + * Set the pipes commands should be piped through before dispatching. + * + * @param array $pipes + * @return $this + */ + public function pipeThrough(array $pipes) + { + // + } +} diff --git a/src/Illuminate/Support/Testing/Fakes/EventFake.php b/src/Illuminate/Support/Testing/Fakes/EventFake.php new file mode 100644 index 000000000000..a1bd3fd0c508 --- /dev/null +++ b/src/Illuminate/Support/Testing/Fakes/EventFake.php @@ -0,0 +1,216 @@ +fired($event, $callback)->count() > 0, + "The expected [{$event}] event was not fired." + ); + } + + /** + * Determine if an event was fired based on a truth-test callback. + * + * @param string $event + * @param callable|null $callback + * @return void + */ + public function assertNotFired($event, $callback = null) + { + PHPUnit::assertTrue( + $this->fired($event, $callback)->count() === 0, + "The unexpected [{$event}] event was fired." + ); + } + + /** + * Get all of the events matching a truth-test callback. + * + * @param string $event + * @param callable|null $callback + * @return \Illuminate\Support\Collection + */ + public function fired($event, $callback = null) + { + if (! $this->hasFired($event)) { + return collect(); + } + + $callback = $callback ?: function () { + return true; + }; + + if (is_null($callback)) { + return collect($this->events[$event]); + } + + return collect($this->events[$event])->filter(function ($arguments) use ($callback) { + return $callback(...$arguments); + })->flatMap(function ($arguments) { + return $this->mapEventArguments($arguments); + }); + } + + /** + * Determine if the given event has been fired. + * + * @param string $event + * @return bool + */ + public function hasFired($event) + { + return isset($this->events[$event]) && ! empty($this->events[$event]); + } + + /** + * Map the "fire" method arguments for inspection. + * + * @param array $arguments + * @return array + */ + protected function mapEventArguments($arguments) + { + if (is_string($arguments[0])) { + return [$arguments[0] => $arguments[1]]; + } else { + return [get_class($arguments[0]) => $arguments[0]]; + } + } + + /** + * Register an event listener with the dispatcher. + * + * @param string|array $events + * @param mixed $listener + * @param int $priority + * @return void + */ + public function listen($events, $listener, $priority = 0) + { + // + } + + /** + * Determine if a given event has listeners. + * + * @param string $eventName + * @return bool + */ + public function hasListeners($eventName) + { + // + } + + /** + * Register an event and payload to be fired later. + * + * @param string $event + * @param array $payload + * @return void + */ + public function push($event, $payload = []) + { + // + } + + /** + * Register an event subscriber with the dispatcher. + * + * @param object|string $subscriber + * @return void + */ + public function subscribe($subscriber) + { + // + } + + /** + * Fire an event until the first non-null response is returned. + * + * @param string $event + * @param array $payload + * @return mixed + */ + public function until($event, $payload = []) + { + return $this->fire($event, $payload, true); + } + + /** + * Flush a set of pushed events. + * + * @param string $event + * @return void + */ + public function flush($event) + { + // + } + + /** + * Fire an event and call the listeners. + * + * @param string|object $event + * @param mixed $payload + * @param bool $halt + * @return array|null + */ + public function fire($event, $payload = [], $halt = false) + { + $name = is_object($event) ? get_class($event) : (string) $event; + + $this->events[$name][] = func_get_args(); + } + + /** + * Get the event that is currently firing. + * + * @return string + */ + public function firing() + { + // + } + + /** + * Remove a set of listeners from the dispatcher. + * + * @param string $event + * @return void + */ + public function forget($event) + { + // + } + + /** + * Forget all of the queued listeners. + * + * @return void + */ + public function forgetPushed() + { + // + } +} diff --git a/src/Illuminate/Support/Testing/Fakes/MailFake.php b/src/Illuminate/Support/Testing/Fakes/MailFake.php new file mode 100644 index 000000000000..420e06d3a38f --- /dev/null +++ b/src/Illuminate/Support/Testing/Fakes/MailFake.php @@ -0,0 +1,216 @@ +sent($mailable, $callback)->count() > 0, + "The expected [{$mailable}] mailable was not sent." + ); + } + + /** + * Assert if a mailable was sent based on a truth-test callback. + * + * @param mixed $users + * @param string $mailable + * @param callable|null $callback + * @return void + */ + public function assertSentTo($users, $mailable, $callback = null) + { + $users = $this->formatRecipients($users); + + return $this->assertSent($mailable, function ($mailable, $to) use ($users, $callback) { + if (! $this->recipientsMatch($users, $this->formatRecipients($to))) { + return false; + } + + if (! is_null($callback)) { + return $callback(...func_get_args()); + } + + return true; + }); + } + + /** + * Format the recipients into a collection. + * + * @param mixed $recipients + * @return \Illuminate\Support\Collection + */ + protected function formatRecipients($recipients) + { + if ($recipients instanceof Collection) { + return $recipients; + } + + return collect(is_array($recipients) ? $recipients : [$recipients]); + } + + /** + * Determine if two given recipient lists match. + * + * @param \Illuminate\Support\Collection $expected + * @param \Illuminate\Support\Collection $recipients + * @return bool + */ + protected function recipientsMatch($expected, $recipients) + { + $expected = $expected->map(function ($expected) { + return is_object($expected) ? $expected->email : $expected; + }); + + return $recipients->map(function ($recipient) { + return is_object($recipient) ? $recipient->email : $recipient; + })->diff($expected)->count() === 0; + } + + /** + * Determine if a mailable was sent based on a truth-test callback. + * + * @param string $mailable + * @param callable|null $callback + * @return void + */ + public function assertNotSent($mailable, $callback = null) + { + PHPUnit::assertTrue( + $this->sent($mailable, $callback)->count() === 0, + "The unexpected [{$mailable}] mailable was sent." + ); + } + + /** + * Get all of the mailables matching a truth-test callback. + * + * @param string $mailable + * @param callable|null $callback + * @return \Illuminate\Support\Collection + */ + public function sent($mailable, $callback = null) + { + if (! $this->hasSent($mailable)) { + return collect(); + } + + $callback = $callback ?: function () { + return true; + }; + + if (is_null($callback)) { + return collect($this->mailables[$mailable]); + } + + return $this->mailablesOf($mailable)->filter(function ($mailable) use ($callback) { + return $callback($mailable->mailable, ...array_values($mailable->getRecipients())); + }); + } + + /** + * Determine if the given mailable has been sent. + * + * @param string $mailable + * @return bool + */ + public function hasSent($mailable) + { + return $this->mailablesOf($mailable)->count() > 0; + } + + /** + * Get all of the mailed mailables for a given type. + * + * @param string $type + * @return \Illuminate\Support\Collection + */ + protected function mailablesOf($type) + { + return collect($this->mailables)->filter(function ($m) use ($type) { + return $m->mailable instanceof $type; + }); + } + + /** + * Begin the process of mailing a mailable class instance. + * + * @param mixed $users + * @return MailableMailer + */ + public function to($users) + { + $this->mailables[] = $mailable = (new MailableFake)->to($users); + + return $mailable; + } + + /** + * Begin the process of mailing a mailable class instance. + * + * @param mixed $users + * @return MailableMailer + */ + public function bcc($users) + { + $this->mailables[] = $mailable = (new MailableFake)->bcc($users); + + return $mailable; + } + + /** + * Send a new message when only a raw text part. + * + * @param string $text + * @param \Closure|string $callback + * @return int + */ + public function raw($text, $callback) + { + // + } + + /** + * Send a new message using a view. + * + * @param string|array $view + * @param array $data + * @param \Closure|string $callback + * @return void + */ + public function send($view, array $data = [], $callback = null) + { + // + } + + /** + * Get the array of failed recipients. + * + * @return array + */ + public function failures() + { + // + } +} diff --git a/src/Illuminate/Support/Testing/Fakes/MailableFake.php b/src/Illuminate/Support/Testing/Fakes/MailableFake.php new file mode 100644 index 000000000000..f8a0897916ef --- /dev/null +++ b/src/Illuminate/Support/Testing/Fakes/MailableFake.php @@ -0,0 +1,70 @@ +sendNow($mailable); + } + + /** + * Send a mailable message immediately. + * + * @param Mailable $mailable + * @return mixed + */ + public function sendNow(Mailable $mailable) + { + $this->mailable = $mailable; + } + + /** + * Push the given mailable onto the queue. + * + * @param Mailable $mailable + * @return mixed + */ + public function queue(Mailable $mailable) + { + return $this->sendNow($mailable); + } + + /** + * Get the recipient information for the mailable. + * + * @return array + */ + public function getRecipients() + { + return ['to' => $this->to, 'cc' => $this->cc, 'bcc' => $this->bcc]; + } +} diff --git a/src/Illuminate/Support/Testing/Fakes/NotificationFake.php b/src/Illuminate/Support/Testing/Fakes/NotificationFake.php new file mode 100644 index 000000000000..3869047ea577 --- /dev/null +++ b/src/Illuminate/Support/Testing/Fakes/NotificationFake.php @@ -0,0 +1,153 @@ +sent($notifiable, $notification, $callback)->count() > 0, + "The expected [{$notification}] notification was not sent." + ); + } + + /** + * Determine if a notification was sent based on a truth-test callback. + * + * @param mixed $notifiable + * @param string $notification + * @param callable|null $callback + * @return void + */ + public function assertNotSent($notifiable, $notification, $callback = null) + { + PHPUnit::assertTrue( + $this->sent($notifiable, $notification, $callback)->count() === 0, + "The unexpected [{$notification}] notification was sent." + ); + } + + /** + * Get all of the notifications matching a truth-test callback. + * + * @param mixed $notifiable + * @param string $notification + * @param callable|null $callback + * @return \Illuminate\Support\Collection + */ + public function sent($notifiable, $notification, $callback = null) + { + if (! $this->hasSent($notifiable, $notification)) { + return collect(); + } + + $callback = $callback ?: function () { + return true; + }; + + $notifications = collect($this->notificationsFor($notifiable, $notification)); + + if (is_null($callback)) { + return $notifications; + } + + return $notifications->filter(function ($arguments) use ($callback) { + return $callback(...array_values($arguments)); + })->pluck('notification'); + } + + /** + * Determine if there are more notifications left to inspect. + * + * @param mixed $notifiable + * @param string $notification + * @return bool + */ + public function hasSent($notifiable, $notification) + { + return ! empty($this->notificationsFor($notifiable, $notification)); + } + + /** + * Get all of the notifications for a notifiable entity by type. + * + * @param mixed $notifiable + * @param string $notification + * @return array + */ + protected function notificationsFor($notifiable, $notification) + { + if (isset($this->notifications[get_class($notifiable)][$notifiable->getKey()][$notification])) { + return $this->notifications[get_class($notifiable)][$notifiable->getKey()][$notification]; + } + + return []; + } + + /** + * Send the given notification to the given notifiable entities. + * + * @param \Illuminate\Support\Collection|array|mixed $notifiables + * @param mixed $notification + * @return void + */ + public function send($notifiables, $notification) + { + return $this->sendNow($notifiables, $notification); + } + + /** + * Send the given notification immediately. + * + * @param \Illuminate\Support\Collection|array|mixed $notifiables + * @param mixed $notification + * @return void + */ + public function sendNow($notifiables, $notification) + { + if (! $notifiables instanceof Collection && ! is_array($notifiables)) { + $notifiables = [$notifiables]; + } + + foreach ($notifiables as $notifiable) { + $notification->id = (string) Uuid::uuid4(); + + $this->notifications[get_class($notifiable)][$notifiable->getKey()][get_class($notification)][] = [ + 'notification' => $notification, + 'channels' => $notification->via($notifiable), + ]; + } + } + + /** + * Get a channel instance by name. + * + * @param string|null $name + * @return mixed + */ + public function channel($name = null) + { + // + } +} diff --git a/src/Illuminate/Support/Testing/Fakes/QueueFake.php b/src/Illuminate/Support/Testing/Fakes/QueueFake.php new file mode 100644 index 000000000000..57ed2cfdf484 --- /dev/null +++ b/src/Illuminate/Support/Testing/Fakes/QueueFake.php @@ -0,0 +1,198 @@ +pushed($job, $callback)->count() > 0, + "The expected [{$job}] job was not pushed." + ); + } + + /** + * Assert if a job was pushed based on a truth-test callback. + * + * @param string $queue + * @param string $job + * @param callable|null $callback + * @return void + */ + public function assertPushedOn($queue, $job, $callback = null) + { + return $this->assertPushed($job, function ($job, $pushedQueue) use ($callback, $queue) { + if ($pushedQueue !== $queue) { + return false; + } + + if ($callback) { + return $callback(...func_get_args()); + } + + return true; + }); + } + + /** + * Determine if a job was pushed based on a truth-test callback. + * + * @param string $job + * @param callable|null $callback + * @return void + */ + public function assertNotPushed($job, $callback = null) + { + PHPUnit::assertTrue( + $this->pushed($job, $callback)->count() === 0, + "The unexpected [{$job}] job was pushed." + ); + } + + /** + * Get all of the jobs matching a truth-test callback. + * + * @param string $job + * @param callable|null $callback + * @return \Illuminate\Support\Collection + */ + public function pushed($job, $callback = null) + { + if (! $this->hasPushed($job)) { + return collect(); + } + + $callback = $callback ?: function () { + return true; + }; + + if (is_null($callback)) { + return collect($this->jobs[$job]); + } + + return collect($this->jobs[$job])->filter(function ($data) use ($callback) { + return $callback($data['job'], $data['queue']); + })->pluck('job'); + } + + /** + * Determine if there are any stored jobs for a given class. + * + * @param string $job + * @return bool + */ + public function hasPushed($job) + { + return isset($this->jobs[$job]) && ! empty($this->jobs[$job]); + } + + /** + * Get the size of the queue. + * + * @param string $queue + * @return int + */ + public function size($queue = null) + { + return 0; + } + + /** + * Push a new job onto the queue. + * + * @param string $job + * @param mixed $data + * @param string $queue + * @return mixed + */ + public function push($job, $data = '', $queue = null) + { + $this->jobs[get_class($job)][] = [ + 'job' => $job, + 'queue' => $queue, + ]; + } + + /** + * Push a raw payload onto the queue. + * + * @param string $payload + * @param string $queue + * @param array $options + * @return mixed + */ + public function pushRaw($payload, $queue = null, array $options = []) + { + // + } + + /** + * Push a new job onto the queue after a delay. + * + * @param \DateTime|int $delay + * @param string $job + * @param mixed $data + * @param string $queue + * @return mixed + */ + public function later($delay, $job, $data = '', $queue = null) + { + return $this->push($job, $data, $queue); + } + + /** + * Push a new job onto the queue. + * + * @param string $queue + * @param string $job + * @param mixed $data + * @return mixed + */ + public function pushOn($queue, $job, $data = '') + { + return $this->push($job, $data, $queue); + } + + /** + * Push a new job onto the queue after a delay. + * + * @param string $queue + * @param \DateTime|int $delay + * @param string $job + * @param mixed $data + * @return mixed + */ + public function laterOn($queue, $delay, $job, $data = '') + { + return $this->push($job, $data, $queue); + } + + /** + * Pop the next job off of the queue. + * + * @param string $queue + * @return \Illuminate\Contracts\Queue\Job|null + */ + public function pop($queue = null) + { + // + } +}