> ## Documentation Index
> Fetch the complete documentation index at: https://docs.subscribd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Test billing flows using the FakeGateway without real API calls

# 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`:

```xml theme={null}
<!-- phpunit.xml.dist -->
<env name="SUBSCRIBD_GATEWAY" value="fake"/>
```

Or configure it per-test in `setUp()`:

```php theme={null}
protected function setUp(): void
{
    parent::setUp();
    config(['subscribd.default' => 'fake']);
}
```

The `fake` gateway is already registered in `config/subscribd.php`:

```php theme={null}
'gateways' => [
    'fake' => ['driver' => \Pixelworxio\Subscribd\Gateways\FakeGateway::class],
],
```

## Creating a subscription in tests

```php theme={null}
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

```php theme={null}
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

```php theme={null}
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

```php theme={null}
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

```php theme={null}
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

```php theme={null}
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:

```php theme={null}
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:

```php theme={null}
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:

```php theme={null}
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:

```php theme={null}
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

* **[Hooks and Events](/advanced/hooks-and-events)** — Full event reference for assertions
* **[Filament Integration](/advanced/filament)** — Testing Filament resources
