›
byrcsc/laravel-mentions · 1.x
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.
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.
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);
});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.
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'),
]);
});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.
The package repository runs:
composer test
composer analyse
vendor/bin/pint --testIts default test connection is in-memory SQLite. CI also runs persistence and
resolution tests against MySQL and PostgreSQL through DB_DRIVER.