BillionaireClubCollc
  • News
  • Notifications
  • Shop
  • Cart
  • Media
  • Advertise with Us
  • Profile
  • Groups
  • Games
  • My Story
  • Chat
  • Contact Us
home shop notifications more
Signin
  •  Profile
  •  Sign Out
Skip to content

Billionaire Club Co LLC

Believe It and You Will Achieve It

Primary Menu
  • Home
  • Politics
  • TSR
  • Anime
  • Michael Jordan vs.Lebron James
  • Crypto
  • Soccer
  • Dating
  • Airplanes
  • Forex
  • Tax
  • New Movies Coming Soon
  • Games
  • CRYPTO INSURANCE
  • Sport
  • MEMES
  • K-POP
  • AI
  • The Bahamas
  • Digital NoMad
  • Joke of the Day
  • RapVerse
  • Stocks
  • SPORTS BETTING
  • Glamour
  • Beauty
  • Travel
  • Celebrity Net Worth
  • TMZ
  • Lotto
  • COVD-19
  • Fitness
  • The Bible is REAL
  • OutDoor Activity
  • Lifestyle
  • Culture
  • Boxing
  • Food
  • LGBTQ
  • Poetry
  • Music
  • Misc
  • Open Source
  • NASA
  • Science
  • Natural & Holstict Med
  • Gardening
  • DYI
  • History
  • Art
  • Education
  • Pets
  • Aliens
  • Astrology
  • Farming and LiveStock
  • LAW
  • Fast & Furious
  • Fishing & Hunting
  • Health
  • Credit Repair
  • Grants
  • All things legal
  • Reality TV
  • Africa Today
  • China Today
  • "DUMB SHIT.."
  • CRYPTO INSURANCE

Using Laravel Facades for Cleaner, Testable Code

For one reason or another, Laravel Facades don't get much love. I often read about how they are not a true facade implementation, and let me tell you, they're not 🤷. They're more like proxies than facades. If you think about it, they simply forward calls to their respective classes, effectively intercepting the request. When you start looking at them this way, you realize that facades, when used correctly, can result in a clean and testable code. So, don't get too caught up in the naming, I mean who cares what they are called? And let's see how we can make use of them.
Same-Same, But Different… But Still Same 😎
When writing service classes, I'm not a fan of using static methods, they make testing dependent classes HARD. However, I do love the clean calls they offer, like Service::action(). With Laravel real-time facades, we can achieve this.
\
Let's take a look at this example:
<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use App\Exceptions\CouldNotFetchRates;
use App\Entities\ExchangeRate;

class ECBExchangeRateService
{
public static function getRatesFromApi(): ExchangeRate
{
$response = Http::get('ecb_url'); // Avoid hardcoding URLs, this is just an example

throw_if($response->failed(), CouldNotFetchRates::apiTimeout('ecb'));

return ExchangeRate::from($response->body());
}
}

\
We have a hyper-simplified service class that attempts to retrieve exchange rates from an API and returns a DTO (Entity, or whatever makes you happy) if everything goes well.
\
Now we can use this service like so:
<?php

namespace App\Classes;

use App\Services\ECBExchangeRateService;

class AnotherClass
{
public function action(): void
{
$rates = ECBExchangeRateService::getRatesFromApi();

// Do something with the rates
}
}

\
The code might look clean, but it's not good; it's not testable. We can write feature tests or integration tests, but when it comes to unit testing this class, we can't. There's no way to mock ECBExchangeRateService::getRatesFromApi(), and unit tests should not have any dependencies (interactions with different classes or systems).
\

Since we are discussing unit tests, I want to emphasize that should not doesn't mean you don't have to. Sometimes it makes sense to have database interaction in unit tests, for example, to test whether or not a relationship is loaded 🤷. Don't follow the rules blindly; sometimes they make sense, sometimes they don't.

\
So, to fix this, we need to follow some steps:

Convert the static getRatesFromApi() into a regular one;
Create a new interface that defines which methods should be implemented by ECBExchangeRateService (optional);
Bind the newly defined interface to our service class in the Laravel service provider (optional);
Make use of dependency injection, whether via a constructor or directly into the method, depending on how you want your API to look.

\
One might argue that this is the correct way to do things, but I'm a very simple guy. I feel that this is an overkill, especially if I know that I won't be changing any implementations for a very long time.
\

I mean, I literally added the autolink to headers today, so I can link only the real-time section of my article. That's how much I want to keep things simple 😂

\
With real-time facades, we can turn the 4 steps into 2:

Convert the static getRatesFromApi() into a regular one (just remove the static keyword);
Prefix the import with the Facades keyword.

\
Your code should look like:
<?php

namespace App\Classes;

use Facades\App\Services\ECBExchangeRateService; // The only change we need

class AnotherClass
{
public function action(): void
{
$rates = ECBExchangeRateService::getRatesFromApi();

// do something with the rates
}
}

\
That's all we needed to do! Removed 1 keyword, and added another. You can't beat this!
\
Here is how we can test our code now:
it('does something with the rates', function () {
ECBExchangeRateService::shouldReceive('getRatesFromApi')->once();

(new AnotherClass)->action();
});

\

I am using Pest.

\
The ECBExchangeRateService will be resolved from the container, just as we would do in the 4 steps above, without the need to create extra interfaces or add more code. We maintain our clean, simple approach and ensure testability. And I know some people still won't agree, dismissing it as dark magic. Well, it's not really magic if it is in the docs; read your docs kids!
Hooot Swappable 🔥
Remember what I mentioned about thinking of facades as proxies? Let's explain it.
\
When using Laravel Queues, we dispatch jobs in our code. When you're testing that code, you're not interested in testing if the actual job is working as expected or not; that can be tested separately. Instead, you're interested in whether or not the job has been dispatched, the number of times it's been dispatched, the payload used, etc. So, to achieve this, we would need two implementations, right? Dispatcher and DispatcherFake - one that actually dispatches the job to Redis, MySQL, or whatever you've set it for, and the second one that does not dispatch anything, but rather captures those events.
\
If we were to implement this ourselves, we would need to follow the 4 steps from earlier, and change the bindings of these implementations depending on the context - if we are running tests or if we are running the actual code. Now, Facades make this much simpler, like really simple. Let's see how.
\
Let's first define our interface:
<?php

namespace App\Contracts;

interface Dispatcher
{
public function dispatch(mixed $job, mixed $handler): mixed
}

\
Then, we can have two implementations:
<?php

namespace App\Bus;

use PHPUnit\Framework\Assert;
use App\Contracts\Dispatcher as DispatcherContract;

class Dispatcher implements DispatcherContract
{
public function dispatch(mixed $job, mixed $handler): mixed
{
// Actually dispatch this to the DB or whatever driver is set
}
}

class DispatcherFake implements DispatcherContract
{
protected $jobs = [];

public function dispatch(mixed $job, mixed $handler): mixed
{
// We are just recording the dispatches here
$this->jobs[$job] = $handler;
}

// We can add testing helpers
public function assertDispatched(mixed $job)
{
Assert::assertTrue(count($this->jobs[$job]) > 0);
}

public function assertDispatchedTimes(mixed $job, int $times = 1)
{
Assert::assertTrue(count($this->jobs[$job]) === $times);
}

// ... and more methods
}

\
Now, instead of resolving implementations manually and having to bind multiple ones, we can make use of facades. They intercept the call, and we can choose where we want to forward it!
<?php

namespace App\Facades;

use App\Bus\DispatcherFake;
use Illuminate\Support\Facades\Facade;

class Dispatcher extends Facade
{
protected static function fake()
{
return tap(new DispatcherFake(), function ($fake) {
// This will set the $resolvedInstance to the faked one
// So every time we try to access the underlying
// implementation, the faked object will be returned instead
static::swap($fake);
});
}

protected static function getFacadeAccessor()
{
return 'dispatcher';
}
}

\

Interested in learning how Facades work under the hood? I've written an article about it.

\
Now we can simply bind our dispatcher to the application container.
<?php

namespace App\Providers;

use App\Bus\Dispatcher;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind('dispatcher', function ($app) {
return new Dispatcher;
});
}

// ...
}

\
And that's it! We can now elegantly swap between implementations, and our code is testable with clean calls, and no injections (but with the same effect).
\
use App\Facades\Dispatcher; // import the facade

it('does dispatches a job', function () {
// This will set the fake implementation as the resolved object
Dispatcher::fake();

// An action that dispatches a job `Dispatcher::dispatch(Job::class, Handler::class);
(new Action)->handle();

// Now you can assert that it has been dispatched
Dispatcher::assertDispatched(Job::class);
});

\
This is a hyper-simplified example, just to see things from a new perspective. Interestingly, this is how your favorite framework tests things internally. So, facades might not be as much of an anti-pattern as you might think. They might be named incorrectly, but you can see how they simplify things.
Eye's open 🔍
Here's a valuable point to consider that might save you a few hours of debugging 🧠.
\
First, let's have a look at the swap method
/**
* Hotswap the underlying instance behind the facade.
*
* @param mixed $instance
* @return void
*/
public static function swap($instance)
{
static::$resolvedInstance[static::getFacadeAccessor()] = $instance;

if (isset(static::$app)) {
static::$app->instance(static::getFacadeAccessor(), $instance); // Binding the faked instance to the container
}
}

\
You can see that when swapping instances, we're not just replacing the cached resolved instance; we're also binding it to the container. This implies that ALL future returned instances, whether using dependency injection or the app() helper, will yield a fake implementation. So, if you're running integration tests, where some classes use constructor injection and not facades, they will also receive the faked implementation. Nevertheless, if you ever call the fake() method, you would expect to receive the fake implementation everywhere anyway. However, it's something to bear in mind when writing tests (especially integration tests), where you might want to use the actual class instead.
Conclusion
Don't fight the framework; embrace it and try to make use of what already exists. There are multiple approaches to each problem, and they can all be good. Don't dismiss something just because someone else thinks otherwise; give it a chance. To me, as long as the code is testable, you're on the right path, you shouldn't worry too much about whether or not it follows certain rules.

Welcome to Billionaire Club Co LLC, your gateway to a brand-new social media experience! Sign up today and dive into over 10,000 fresh daily articles and videos curated just for your enjoyment. Enjoy the ad free experience, unlimited content interactions, and get that coveted blue check verification—all for just $1 a month!

Source link

Share
What's your thought on the article, write a comment
0 Comments
×

Sign In to perform this Activity

Sign in
×

Account Frozen

Your account is frozen. You can still view content but cannot interact with it.

Please go to your settings to update your account status.

Open Profile Settings

Ads

  • Billionaire128 Liquid Gold Men’s Athletic Long Shorts

    $ 40.00
  • Billionaire128 Liquid Gold Flip-Flops

    $ 18.00
  • Original Billionaire128 Basic Pillow

    $ 26.50
  • News Social

    • Facebook
    • Twitter
    • Facebook
    • Twitter
    Copyright © 2024 Billionaire Club Co LLC. All rights reserved