Mocking Third Party Libraries

A simple way to test components that depend on third party libraries without making any api calls.

If you have ever written any tests for components that depend on a third party library, most likely you would have also set up test data first either manually in the third party’s test environment or via fixtures by your tests. This can get cumbersome.

Take Stripe for example. You would set up test plans, customers, subscriptions, invoices, and more within Stripe’s test mode. While there are ways to make the process better, such as using Stripe’s mock HTTP server, here we propose a simpler way using a feature offered by Laravel called facades. The principle can be applied to any library, not just Stripe.

According to Laravel documentation:

Laravel facades serve as “static proxies” to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.

The first step is to register the Stripe API library as a singleton in the service container. The benefit of this is you only have to set API keys once. We do so with a “wrapper” class that will forward all calls to the library.

namespace App\Wrappers;
class StripeWrapper {
    public function __construct($secret) { \Stripe\Stripe::setApiKey($secret); } 
    /** * Wrapper around the various Stripe classes. 
     * Example: * $stripeWrapper->Invoice('upcoming', ['customer' => $stripeId])
     *      gets forwarded to
     *      \Stripe\Invoice::upcoming(['customer' => $stripeId]);
     */

    public function __call($class, $arguments)
    {
        $class = "\\Stripe\\$class";
        if (! class_exists($class)) {
            throw new \Exception("$class does not exist");
        }

        if (! count($arguments)) {
            throw new \Exception("Missing method name");
        }

        $method = $arguments[0];
        if (! method_exists($class, $method)) {
            throw new \Exception("$class::$method does not exist");
        }

        $arguments = count($arguments) > 1 ? array_slice($arguments, 1) : [];
        return call_user_func_array([$class, $method], $arguments);
    }
}

Next, bind it by adding the following in the boot() method of app/Providers/AppServiceProvider.php:

$this->app->singleton(App\Wrappers\StripeWrapper::class, function ($app) {
    return new App\Wrappers\StripeWrapper(config('services.stripe.secret'));
});
$this->app->alias(App\Wrappers\StripeWrapper::class, 'stripe_wrapper');

Finally, create the facade.

<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Stripe extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'stripe_wrapper';
    }
}

Now, whenever you need to make a Stripe call, you simply use the facade instead of the library.

use App\Facades\Stripe;
// ...
Stripe::Invoice('upcoming', ['customer' => $stripeId]);

One of the great things about facades is the ability to mock them. When writing a test that fetches an upcoming invoice, you can mock what the response would look like.

use App\Facades\Stripe;
// ...
public function testUpcomingInvoice()
{
    Stripe::shouldReceive('Invoice')
            ->once()
            ->with('upcoming', ['customer' => 'cus_XXX'])
            ->andReturn($mockedResponse);
    // ...
}

Voila! You can do this with any PHP SDK. Just stick it inside a wrapper! The syntax may vary from library to library but the result is the same.

Posted in Categories Blog