---
title: "Testing — Sluggable for Laravel | Roundly"
description: "Test slug generation, events, history redirects and queued regeneration in your own Pest suite through the public API — no special setup."
url: https://roundly-consulting.com/open-source/docs/sluggable-for-laravel/testing
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Sluggable for Laravel](https://roundly-consulting.com/open-source/docs/sluggable-for-laravel.md)

# Testing

Slugs are generated in normal model events, so feature tests need no special setup — create models and assert through the public API:

```php
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Sluggable\Enums\ChangeReason;
use RoundlyConsulting\Sluggable\Events\SlugChanged;

it('generates unique slugs from the name', function () {
    expect(Clinic::create(['name' => 'Happy Paws'])->slug)->toBe('happy-paws');
    expect(Clinic::create(['name' => 'Happy Paws'])->slug)->toBe('happy-paws-2');
});

it('announces a manual slug change', function () {
    Event::fake([SlugChanged::class]);

    $clinic = Clinic::create(['name' => 'Happy Paws']);
    $clinic->update(['slug' => 'happy-paws-vienna']);

    Event::assertDispatched(SlugChanged::class, fn (SlugChanged $event): bool =>
        $event->current === 'happy-paws-vienna' && $event->reason === ChangeReason::Manual);
});

it('redirects a retired slug to the current one', function () {
    // Product: ->regenerateOnUpdate()->keepHistory()->routeKey(); Route::get('/products/{product}', …)
    $product = Product::create(['name' => 'Classic Chair']);
    $product->update(['name' => 'Heritage Chair']);

    $this->get('/products/classic-chair')->assertStatus(301)->assertRedirect('/products/heritage-chair');
});
```

- Seed large factories inside Slugs::withoutGeneration() when the slug does not matter — it skips generation, events and history.
- History tests need the published sluggable-migrations in your migrations directory, so RefreshDatabase creates the slug\_history table.
- Use Slugs::generate($model) to assert the value a model would get without saving anything.

## Queued regeneration

There is no Slugs::fake(): generation is deterministic and runs against your own database, so assert on the saved rows. Around queueRegeneration() use Laravel’s Bus::fake() and assert the RegenerateSlugsJob dispatch:

```php
use Illuminate\Support\Facades\Bus;
use RoundlyConsulting\Sluggable\Enums\RegenerationMode;
use RoundlyConsulting\Sluggable\Facades\Slugs;
use RoundlyConsulting\Sluggable\Jobs\RegenerateSlugsJob;

it('queues a full regeneration', function () {
    Product::create(['name' => 'Classic Chair']);
    Bus::fake();

    Slugs::model(Product::class)->queueRegeneration(mode: RegenerationMode::All);

    Bus::assertDispatched(RegenerateSlugsJob::class);
});
```

## The package’s own suite

```bash
composer test            # Pest
composer test-coverage   # Pest with a 90 % coverage floor
composer analyse         # Larastan level 7
```

The Pest suite runs against SQLite, PostgreSQL and MySQL, including real concurrent-write races and HTTP-level redirect tests.

## Static analysis

The package ships a PHPStan extension (extension.neon, auto-included with phpstan/extension-installer) that makes $table->slug(), $table->localizedSlug(), $table->uniqueSlug(), Rule::uniqueSlug() and Rule::validSlug() known to Larastan.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/sluggable-for-laravel.md)
