Back to blog

Livewire Component Testing: Ensuring Your Interactive UIs Work Flawlessly

Livewire Testing Pest Quality Assurance

Testing Livewire components can seem daunting, but Pest makes it delightful. When you're building interactive UIs, thorough testing becomes crucial—you're testing not just state, but user interactions and reactive updates.

Setting Up Livewire Tests

Pest provides a dedicated test class for Livewire components:

use App\Livewire\PostForm;
use Livewire\Livewire;

test('can submit a post form', function () {
    Livewire::test(PostForm::class)
        ->set('title', 'My First Post')
        ->set('content', 'This is exciting!')
        ->call('submit')
        ->assertSuccessful();
});

This reads naturally and tests the entire interaction flow.

Testing State Changes

Verify that component state updates correctly:

test('updates post title on input', function () {
    Livewire::test(PostForm::class)
        ->set('title', 'Original Title')
        ->assertSet('title', 'Original Title')
        ->set('title', 'Updated Title')
        ->assertSet('title', 'Updated Title');
});

Testing Method Calls

Call component methods and verify results:

test('increments counter when button clicked', function () {
    Livewire::test(Counter::class)
        ->assertSet('count', 0)
        ->call('increment')
        ->assertSet('count', 1)
        ->call('increment')
        ->assertSet('count', 2);
});

Testing Validation

Ensure validation works correctly:

test('validates email field', function () {
    Livewire::test(ContactForm::class)
        ->set('email', 'invalid-email')
        ->call('submit')
        ->assertHasErrors('email');
});

test('accepts valid email', function () {
    Livewire::test(ContactForm::class)
        ->set('email', 'test@example.com')
        ->call('submit')
        ->assertHasNoErrors('email');
});

Testing Rendered Output

Verify that your component renders expected content:

test('displays published posts', function () {
    Post::factory(3)->create(['published' => true]);
    Post::factory(2)->create(['published' => false]);

    Livewire::test(PostList::class)
        ->assertViewHas('posts', fn($posts) => $posts->count() === 3)
        ->assertSee('My First Post');
});

Testing Conditional Rendering

Test that elements appear or disappear based on state:

test('shows error message when validation fails', function () {
    Livewire::test(LoginForm::class)
        ->set('email', 'invalid')
        ->call('submit')
        ->assertSee('The email field must be a valid email address');
});

Real-World Example: Search Component

Here's a complete test suite for a search component:

describe('SearchPosts', function () {
    test('displays all posts when no search term', function () {
        Post::factory(5)->create();

        Livewire::test(SearchPosts::class)
            ->assertViewHas('posts', fn($posts) => $posts->count() === 5);
    });

    test('filters posts by search term', function () {
        Post::factory()->create(['title' => 'Laravel Tips']);
        Post::factory()->create(['title' => 'PHP Basics']);

        Livewire::test(SearchPosts::class)
            ->set('query', 'Laravel')
            ->assertViewHas('posts', fn($posts) => $posts->count() === 1)
            ->assertSee('Laravel Tips')
            ->assertDontSee('PHP Basics');
    });

    test('debounces search input', function () {
        Post::factory(10)->create();

        Livewire::test(SearchPosts::class)
            ->set('query', 'test')
            ->assertDispatched('search'); // Only one dispatch for debounced input
    });

    test('resets search when cleared', function () {
        Post::factory(5)->create();

        Livewire::test(SearchPosts::class)
            ->set('query', 'something')
            ->set('query', '')
            ->assertViewHas('posts', fn($posts) => $posts->count() === 5);
    });
});

Testing Livewire Events

Test that your component dispatches and listens to events:

test('dispatches post-created event', function () {
    Livewire::test(PostForm::class)
        ->set('title', 'New Post')
        ->call('submit')
        ->assertDispatched('post-created');
});

test('listens for post-deleted event', function () {
    $post = Post::factory()->create();

    Livewire::test(PostList::class)
        ->emit('post-deleted', $post->id)
        ->assertViewHas('posts', fn($posts) => $posts->doesntContain($post));
});

Best Practices

  1. Test behavior, not implementation: Focus on what users see and do
  2. Keep tests focused: One assertion per test when possible
  3. Use factories: Create realistic test data with factories
  4. Test edge cases: Empty states, validation errors, etc.
  5. Test integrations: Ensure components work with real database and models

Coverage Goals

Aim for 80%+ coverage on your Livewire components. They're critical to user experience, so thorough testing pays dividends.

Conclusion

Testing Livewire components with Pest is intuitive and powerful. By testing your interactive UIs thoroughly, you catch bugs early and deploy with confidence. Your users will thank you for the reliability.

You Might Also Like