Fake Facade in Laravel
Tags: laravel, designpatterns, testing, facade
Published: 2023-12-26 21:13:27

Generally, when we work with services within our application that interact with the external world, for example: Sending emails, queued processing, payment providers, it can end up making it difficult to implement automated tests in our application, it is slow and unfeasible to trigger all emails Once your tests run, make a request to a payment gateway, trigger a job to your real queue.

In this article I will talk about an implementation pattern that gets around this problem by facilitating the implementation of automated tests with an example of a payment provider implementation.

## What is a Facade?

Facade is a design pattern that allows us to implement a simple interface to abstract a complex system.

### Laravel Examples

- [Mail](https://laravel.com/docs/10.x/mail)
- [Queue](https://laravel.com/docs/10.x/queues)


As users of the framework, we do not need to implement email triggering functionality from scratch, queue management, connection to the Database, this is already well abstracted by the framework using Facades.

**Email trigger example**

```php
Mail::to('email@here.com')->send(new EmailContent);
```

That said, how would we test whether the email is actually being fired using automated tests?

An alternative is to run the tests with our personal email and check our inbox, but that just thinking about it doesn't seem very efficient.

For that there is this **Fake Facade** pattern, whose name I invented because I couldn't find a name for it.

If you have already created tests for email dispatching, you probably already know the fake method of this facade:


```php
Mail::fake();
```

Not all facades implement this method but the objective of this article is to understand what it does and implement it ourselves.

![Print of Mail class implementation in laravel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4j1g73rrq6k9zy02zqb1.png)

Looking at what the method does we can see that it basically calls the swap method which basically swaps and is behind this Facade.

So basically, before we call the method, the Mail class points to the real implementation of sending emails and after calling the method it points to this `MailFake` class.

## What does this MailFake class do?

Firstly, it needs to implement all the methods that the real implementation has so that no problems occur during the execution of your program.

Let's go back to the example:

```php
Mail::to('email@here.com')->send(new EmailContent);
```

In the **MailFake** class, the send method actually just adds the email to an array so we can test this in the future, example:

```php
function dispatchEmail() {
	Mail::to('email@here.com')->send(new EmailContent);
}

it('should dispatch email successfully', function () {
	// Arrange
	Mail::fake();

	// Act
	dispatchEmail();
	
	// Assert
	Mail::assertSent(EmailContent::class);
});
```

Now that we understand what the implementation is for and how to use it, let's create our own implementation.

One case where this implementation is viable is integration with payment providers, let's go.

## Step 1: Let's create our contract (interface)

```php

interface PaymentProviderContract
{
	charge(Invoice $invoice): Charging;
}

```

## Step 2: Create our Facade

```php

class PaymentProvider extends Facade
{
	public function getFacadeAccessor()
	{
		return PaymentProviderContract::class;
	}
}

```

## Step 3: We create our actual payment provider implementation

```php
class StripePaymentProvider implements PaymentProviderContract
{
	public function charge(Invoice $invoice): Charging
	{
		// Interacts with the stripe api
		return new Charging();
	}
}
```

## Step 4: We point Facade towards implementing Stripe

We can do this in our **AppServiceProvider** in the **register** method.

```php

class AppServiceProvider extends ServiceProvider
{
	public function register()
	{
		$this->app->register(
			PaymentProviderContract::class,
			fn () => new StripePaymentProvider,
		)
	}
} 

```

Now every time we call `PaymentProvider::charge()` it will point to the Stripe implementation because we declared this in our Service container.

## Step 5: We create our fake implementation

```php

class FakePaymentProvider implements PaymentProviderContract
{
	private array $chargings = [];

	public function charge(Invoice $invoice): Charging
	{
		$charging = new Charging;
		
                // Fill charging with invoice informations
	
		$this->chargings[] = $charging;
	}

	public function assertChargingsCount(int $count): void
	{
		expect($this->chargings)->toHaveCount($count);
	}
}

```

## Step 6: Implement the method to perform the swap on our facade

```php

class PaymentProvider extends Facade
{
	public static function fake(): FakePaymentProvider
	{
		static::swap($fake = new FakePaymentProvider)
		
		return $fake;
	}

	public function getFacadeAccessor()
	{
		return PaymentProviderContract::class;
	}
}

```

Okay, now we can safely test our application

```php

function chargeUser(User $user): Charging
{
	return PaymentProvider::charge($user);
}

it('should charge a user', function () {
	// Arrange
	PaymentProvider::fake(); // Swap for fake class

	// Act
	chargeUser(); // now the charge method will be executed by the fake class
	// Assert
	PaymentProvider::assertChargingsCount(1);
});
```
Okay, now with this pattern implemented we can test only the rules of our application without needing to "test other people's code" which would mean testing Stripe's Api, for example.

We can and should create more auxiliary methods in our FakePaymentProvider class to improve the quality of development and also the effectiveness of our tests.