Skip to main content

Testing

Subscribd ships a fake gateway driver that processes all billing operations synchronously in-memory, with no HTTP calls. Use it in your test suite to test subscription creation, plan swaps, cancellations, invoices, and entitlements without mocking.

Setup

Set SUBSCRIBD_GATEWAY=fake in your phpunit.xml or pest.config.php:
<!-- phpunit.xml.dist -->
<env name="SUBSCRIBD_GATEWAY" value="fake"/>
Or configure it per-test in setUp():
protected function setUp(): void
{
    parent::setUp();
    config(['subscribd.default' => 'fake']);
}
The fake gateway is already registered in config/subscribd.php:
'gateways' => [
    'fake' => ['driver' => \Pixelworxio\Subscribd\Gateways\FakeGateway::class],
],

Creating a subscription in tests

use Pixelworxio\Subscribd\Actions\CreateSubscription;
use Pixelworxio\Subscribd\Models\Plan;

it('subscribes a user to the pro plan', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro', 'amount' => 4900, 'interval' => 'month']);

    app(CreateSubscription::class)->execute($user, $plan);

    expect($user->subscribed())->toBeTrue();
    expect($user->subscription()->plan->key)->toBe('pro');
    expect($user->subscription()->status->value)->toBe('active');
});

Testing plan swaps

use Pixelworxio\Subscribd\Actions\SwapPlan;

it('swaps to a higher tier plan', function () {
    $user    = User::factory()->create();
    $starter = Plan::factory()->create(['key' => 'starter', 'amount' => 1900]);
    $pro     = Plan::factory()->create(['key' => 'pro',     'amount' => 4900]);

    app(CreateSubscription::class)->execute($user, $starter);
    app(SwapPlan::class)->execute($user->subscription(), $pro);

    expect($user->fresh()->subscription()->plan->key)->toBe('pro');
});

Testing cancellation

use Pixelworxio\Subscribd\Actions\CancelSubscription;

it('enters grace period on cancel at period end', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro']);

    app(CreateSubscription::class)->execute($user, $plan);
    app(CancelSubscription::class)->execute($user->subscription());

    expect($user->fresh()->onGracePeriod())->toBeTrue();
    expect($user->fresh()->subscribed())->toBeTrue();   // still has access
});

it('loses access immediately on immediate cancel', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro']);

    app(CreateSubscription::class)->execute($user, $plan);
    app(CancelSubscription::class)->execute($user->subscription(), immediately: true);

    expect($user->fresh()->subscribed())->toBeFalse();
});

Testing invoices

it('creates an invoice on subscription', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro', 'amount' => 4900]);

    app(CreateSubscription::class)->execute($user, $plan);

    $invoice = $user->latestInvoice();

    expect($invoice)->not->toBeNull();
    expect($invoice->status->value)->toBe('paid');
    expect($invoice->total()->getAmount()->toInt())->toBe(4900);
});

Testing entitlements

use Pixelworxio\Subscribd\Facades\Entitlements;

it('grants feature access on active subscription', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create([
        'key'      => 'pro',
        'features' => ['exports' => true, 'projects' => 10],
    ]);

    app(CreateSubscription::class)->execute($user, $plan);

    expect(Entitlements::for($user)->allows('exports'))->toBeTrue();
    expect(Entitlements::for($user)->limit('projects'))->toBe(10);
    expect(Entitlements::for($user)->allows('exports'))->toBeTrue();
});

Testing trials

use Pixelworxio\Subscribd\Actions\ConvertTrialToActive;
use Pixelworxio\Subscribd\Actions\ExtendTrial;

it('starts a trial', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro', 'trial_days' => 14]);

    app(CreateSubscription::class)->execute($user, $plan);

    expect($user->onTrial())->toBeTrue();
    expect($user->subscription()->trial_ends_at->isFuture())->toBeTrue();
});

it('converts a trial to active', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro', 'trial_days' => 14]);

    app(CreateSubscription::class)->execute($user, $plan);
    app(ConvertTrialToActive::class)->execute($user->subscription());

    expect($user->fresh()->onTrial())->toBeFalse();
    expect($user->fresh()->subscription()->status->value)->toBe('active');
});

Testing events

Use Laravel’s Event::fake() to assert events are fired:
use Pixelworxio\Subscribd\Events\SubscriptionCreated;
use Pixelworxio\Subscribd\Events\SubscriptionCanceled;
use Illuminate\Support\Facades\Event;

it('fires SubscriptionCreated when subscribing', function () {
    Event::fake([SubscriptionCreated::class]);

    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro']);

    app(CreateSubscription::class)->execute($user, $plan);

    Event::assertDispatched(SubscriptionCreated::class, function ($event) use ($user) {
        return $event->subscription->billable->is($user);
    });
});

Simulating payment failure

The FakeGateway can be instructed to fail on the next operation:
use Pixelworxio\Subscribd\Gateways\FakeGateway;

it('handles payment failure gracefully', function () {
    FakeGateway::shouldFail();

    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro']);

    expect(fn () => app(CreateSubscription::class)->execute($user, $plan))
        ->toThrow(\Pixelworxio\Subscribd\Exceptions\PaymentFailedException::class);
});

Time travel

Use Laravel’s $this->travel() or Pest’s freeze() to test time-dependent billing behaviour:
it('expires a trial after trial_days', function () {
    $user = User::factory()->create();
    $plan = Plan::factory()->create(['key' => 'pro', 'trial_days' => 14]);

    app(CreateSubscription::class)->execute($user, $plan);

    $this->travel(15)->days();

    expect($user->fresh()->onTrial())->toBeFalse();
});

Using factories

Subscribd ships model factories for all its models. Use them directly in tests:
use Pixelworxio\Subscribd\Models\Subscription;
use Pixelworxio\Subscribd\Models\Invoice;
use Pixelworxio\Subscribd\Models\Coupon;

$subscription = Subscription::factory()->active()->for($user, 'billable')->create();
$invoice      = Invoice::factory()->paid()->for($subscription)->create();
$coupon       = Coupon::factory()->create(['code' => 'SAVE20', 'percent_off' => 20]);

Next steps