›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-mentions
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Mention records
  • Parsing text
  • Resolving targets
  • Synchronization

Operations

  • Scan multiple attributes
  • Use markup mentions
  • Mention groups
  • Querying mentions
  • React to lifecycle events
  • Control synchronization
  • Extend the package

Reference

  • Configuration
  • Public API
  • Published assets
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Mention records
  • Parsing text
  • Resolving targets
  • Synchronization

Operations

  • Scan multiple attributes
  • Use markup mentions
  • Mention groups
  • Querying mentions
  • React to lifecycle events
  • Control synchronization
  • Extend the package

Reference

  • Configuration
  • Public API
  • Published assets
  • Testing
  • Troubleshooting

byrcsc/laravel-mentions · 1.x

Testing.

Test synchronization, unresolved candidates, queries, and lifecycle events with Laravel helpers.

Test the package through your Eloquent models. The source trait uses normal database relations and Laravel events, so application tests need no package test double.

Test automatic synchronization

Create the target first, then save source text:

use App\Models\Comment;
use App\Models\User;

it('stores a user mention', function (): void {
    $jane = User::factory()->create(['name' => 'jane']);

    $comment = Comment::factory()->create([
        'body' => 'Hello @jane',
    ]);

    expect($comment->mentions()->count())->toBe(1)
        ->and($comment->mentioned()->first()->is($jane))->toBeTrue();
});

Use RefreshDatabase or your existing database reset strategy. The package migration must run in the test environment.

Test a synchronization diff

Disable automatic sync on the test model or use saveQuietly() before the explicit call:

use App\Models\Comment;
use App\Models\User;

it('reports added and removed mentions', function (): void {
    User::factory()->create(['name' => 'jane']);
    User::factory()->create(['name' => 'john']);

    $comment = Comment::factory()->create(['body' => '@jane']);

    $comment->body = '@john and @missing';
    $comment->saveQuietly();

    $result = $comment->syncMentions();

    expect($result->created)->toHaveCount(1)
        ->and($result->removed)->toHaveCount(1)
        ->and($result->retained)->toBe([])
        ->and($result->unresolved)->toHaveCount(1);
});

Test lifecycle events

Fake only the package events under test:

use App\Models\Comment;
use App\Models\User;
use Byrcsc\Mentions\Events\MentionCreated;
use Byrcsc\Mentions\Events\MentionRemoved;
use Illuminate\Support\Facades\Event;

it('dispatches one event for a new target', function (): void {
    User::factory()->create(['name' => 'jane']);
    Event::fake([MentionCreated::class, MentionRemoved::class]);

    Comment::factory()->create(['body' => '@jane']);

    Event::assertDispatchedTimes(MentionCreated::class, 1);
    Event::assertNotDispatched(MentionRemoved::class);
});

Saving unchanged text does not dispatch another created event. Deleting the source does not dispatch a removed event.

Test parser behavior directly

Resolve the concrete parser when testing custom text boundaries:

use Byrcsc\Mentions\Parsers\RegexMentionParser;
use Byrcsc\Mentions\ValueObjects\MentionCandidate;

it('does not parse an email domain', function (): void {
    $parser = app(RegexMentionParser::class);

    expect($parser->parse('jane@example.com'))->toBe([])
        ->and($parser->parse('Hello @jane'))->toEqual([
            MentionCandidate::fromHandle('@jane', 'jane'),
        ]);
});

Test query scopes

Assert the target type as well as the ID by creating another target class with the same key:

use App\Models\Comment;
use App\Models\User;

$jane = User::factory()->create(['name' => 'jane']);
$john = User::factory()->create(['name' => 'john']);
$expectedComment = Comment::factory()->create(['body' => '@jane']);
Comment::factory()->create(['body' => '@john']);

$comments = Comment::query()->whereMentions($jane)->get();

expect($comments->modelKeys())->toBe([$expectedComment->getKey()]);

Use with('source') when testing reverse-query iteration across several source types. This mirrors the production query path and exposes accidental N+1 queries.

Package contributor checks

The package repository runs:

composer test
composer analyse
vendor/bin/pint --test

Its default test connection is in-memory SQLite. CI also runs persistence and resolution tests against MySQL and PostgreSQL through DB_DRIVER.

What to read next

  • Synchronization for each result bucket.
  • React to lifecycle events for event timing.
  • Troubleshooting for failures to reproduce in a test.
PreviousPublished assetsNextTroubleshooting
View source

On this page

  1. Test automatic synchronization
  2. Test a synchronization diff
  3. Test lifecycle events
  4. Test parser behavior directly
  5. Test query scopes
  6. Package contributor checks
  7. What to read next