Skip to content

测试

Livewire 组件很容易测试。因为它们底层只是 Laravel 类,可以使用 Laravel 现有的测试工具进行测试。然而,Livewire 提供了许多额外的工具,使测试你的组件变得轻而易举。

本文档将指导你使用 Pest 作为推荐的测试框架来测试 Livewire 组件,当然如果你愿意,也可以使用 PHPUnit。

安装 Pest

Pest 是一个令人愉悦的 PHP 测试框架,专注于简洁性。它是 Livewire 4 中测试 Livewire 组件的推荐方式。

要在 Laravel 应用程序中安装 Pest,首先删除 PHPUnit(如果已安装)并引入 Pest:

shell
composer remove phpunit/phpunit
composer require pestphp/pest --dev --with-all-dependencies

接下来,在你的项目中初始化 Pest:

shell
./vendor/bin/pest --init

这将在你的项目中创建一个 tests/Pest.php 配置文件。

有关更详细的安装说明,请参阅 Pest 安装文档

为基于视图的组件配置 Pest

如果你在基于视图的组件(单文件或多文件)旁边编写测试,你需要配置 Pest 来识别这些测试文件。

首先,更新你的 tests/Pest.php 文件以包含 resources/views 目录:

php
pest()->extend(Tests\TestCase::class)
    // ...
    ->in('Feature', '../resources/views');

这告诉 Pest 对在 tests/Feature 目录和 resources/views 中任何位置找到的测试使用你的 TestCase 基类。

接下来,更新你的 phpunit.xml 文件以包含组件测试的测试套件:

xml
<testsuite name="Components">
    <directory suffix=".test.php">resources/views</directory>
</testsuite>

现在当你运行 ./vendor/bin/pest 时,Pest 将识别并运行位于组件旁边的测试。

创建你的第一个测试

你可以通过在 make:livewire 命令后附加 --test 标志来生成与组件一起的测试文件:

shell
php artisan make:livewire post.create --test

对于多文件组件,这将在 resources/views/components/post/create.test.php 创建一个测试文件:

php
<?php

use Livewire\Livewire;

it('renders successfully', function () {
    Livewire::test('post.create')
        ->assertStatus(200);
});

对于基于类的组件,这会在 tests/Feature/Livewire/Post/CreateTest.php 创建一个 PHPUnit 测试文件。你可以将其转换为 Pest 语法或继续使用 PHPUnit——两者都能很好地与 Livewire 配合使用。

测试页面包含组件

你可以编写的最简单的 Livewire 测试是断言给定的端点包含并成功渲染了一个 Livewire 组件。

php
it('component exists on the page', function () {
    $this->get('/posts/create')
        ->assertSeeLivewire('post.create');
});

冒烟测试提供巨大价值

像这样的测试被称为"冒烟测试"——它们确保你的应用程序没有灾难性问题。虽然简单,但这些测试提供了巨大的价值,因为它们几乎不需要维护,并且给你一个基本的信心,确保你的页面成功渲染。

浏览器测试

Pest v4 包含由 Playwright 驱动的一流浏览器测试支持。这允许你在真实浏览器中测试你的 Livewire 组件,像用户一样与它们交互。

安装浏览器测试

首先,安装 Pest 浏览器插件:

shell
composer require pestphp/pest-plugin-browser --dev

接下来,通过 npm 安装 Playwright:

shell
npm install playwright@latest
npx playwright install

有关完整的浏览器测试文档,请参阅 Pest 浏览器测试指南

编写浏览器测试

你可以使用 Livewire::visit() 而不是 Livewire::test() 在真实浏览器中测试你的组件:

php
it('can create a new post', function () {
    Livewire::visit('post.create')
        ->type('[wire\:model="title"]', 'My first post')
        ->type('[wire\:model="content"]', 'This is the content')
        ->press('Save')
        ->assertSee('Post created successfully');
});

浏览器测试比单元测试慢,但提供端到端的信心,确保你的组件在真实浏览器环境中按预期工作。

有关可用浏览器测试断言的完整列表,请参阅 Pest 浏览器测试断言

何时使用浏览器测试

对关键用户流程和复杂交互使用浏览器测试。对于大多数组件测试,标准的 Livewire::test() 方法更快且足够。

测试视图

Livewire 提供 assertSee() 来验证文本出现在组件的渲染输出中:

php
use App\Models\Post;

it('displays posts', function () {
    Post::factory()->create(['title' => 'My first post']);
    Post::factory()->create(['title' => 'My second post']);

    Livewire::test('show-posts')
        ->assertSee('My first post')
        ->assertSee('My second post');
});

断言视图数据

有时测试传递给视图的数据而不是渲染输出会很有帮助:

php
use App\Models\Post;

it('passes all posts to the view', function () {
    Post::factory()->count(3)->create();

    Livewire::test('show-posts')
        ->assertViewHas('posts', function ($posts) {
            return count($posts) === 3;
        });
});

对于简单的断言,你可以直接传递期望的值:

php
Livewire::test('show-posts')
    ->assertViewHas('postCount', 3);

使用身份验证测试

大多数应用程序需要用户登录。与其在每个测试开始时手动进行身份验证,不如使用 actingAs() 方法:

php
use App\Models\User;
use App\Models\Post;

it('user only sees their own posts', function () {
    $user = User::factory()
        ->has(Post::factory()->count(3))
        ->create();

    $stranger = User::factory()
        ->has(Post::factory()->count(2))
        ->create();

    Livewire::actingAs($user)
        ->test('show-posts')
        ->assertViewHas('posts', function ($posts) {
            return count($posts) === 3;
        });
});

测试属性

Livewire 提供了设置和断言组件属性的工具。

使用 set() 更新属性,使用 assertSet() 验证其值:

php
it('can set the title property', function () {
    Livewire::test('post.create')
        ->set('title', 'My amazing post')
        ->assertSet('title', 'My amazing post');
});

初始化属性

组件通常从父组件或路由参数接收数据。将此数据作为第二个参数传递给 Livewire::test()

php
use App\Models\Post;

it('title field is populated when editing', function () {
    $post = Post::factory()->create([
        'title' => 'Existing post title',
    ]);

    Livewire::test('post.edit', ['post' => $post])
        ->assertSet('title', 'Existing post title');
});

设置 URL 参数

如果你的组件使用 Livewire 的 URL 功能 来跟踪查询字符串中的状态,使用 withQueryParams() 来模拟 URL 参数:

php
use App\Models\Post;

it('can search posts via url query string', function () {
    Post::factory()->create(['title' => 'Laravel testing']);
    Post::factory()->create(['title' => 'Vue components']);

    Livewire::withQueryParams(['search' => 'Laravel'])
        ->test('search-posts')
        ->assertSee('Laravel testing')
        ->assertDontSee('Vue components');
});

设置 Cookies

使用 withCookie()withCookies() 为你的测试设置 cookies:

php
it('loads discount token from cookie', function () {
    Livewire::withCookies(['discountToken' => 'SUMMER2024'])
        ->test('cart')
        ->assertSet('discountToken', 'SUMMER2024');
});

调用操作

使用 call() 方法在测试中触发组件操作:

php
use App\Models\Post;

it('can create a post', function () {
    expect(Post::count())->toBe(0);

    Livewire::test('post.create')
        ->set('title', 'My new post')
        ->set('content', 'Post content here')
        ->call('save');

    expect(Post::count())->toBe(1);
});

Pest 期望

上面的示例使用 Pest 的 expect() 语法进行断言。有关可用期望的完整列表,请参阅 Pest 期望文档

你可以向操作传递参数:

php
Livewire::test('post.show')
    ->call('deletePost', $postId);

测试验证

使用 assertHasErrors() 断言验证错误已被抛出:

php
it('title field is required', function () {
    Livewire::test('post.create')
        ->set('title', '')
        ->call('save')
        ->assertHasErrors('title');
});

测试特定的验证规则:

php
it('title must be at least 3 characters', function () {
    Livewire::test('post.create')
        ->set('title', 'ab')
        ->call('save')
        ->assertHasErrors(['title' => ['min:3']]);
});

测试授权

使用 assertUnauthorized()assertForbidden() 确保授权检查正常工作:

php
use App\Models\User;
use App\Models\Post;

it('cannot update another users post', function () {
    $user = User::factory()->create();
    $stranger = User::factory()->create();
    $post = Post::factory()->for($stranger)->create();

    Livewire::actingAs($user)
        ->test('post.edit', ['post' => $post])
        ->set('title', 'Hacked!')
        ->call('save')
        ->assertForbidden();
});

测试重定向

断言操作执行了重定向:

php
it('redirects to posts index after creating', function () {
    Livewire::test('post.create')
        ->set('title', 'New post')
        ->set('content', 'Content here')
        ->call('save')
        ->assertRedirect('/posts');
});

你还可以断言重定向到命名路由或页面组件:

php
->assertRedirect(route('posts.index'));
->assertRedirectToRoute('posts.index');

测试事件

断言从组件分发了事件:

php
it('dispatches event when post is created', function () {
    Livewire::test('post.create')
        ->set('title', 'New post')
        ->call('save')
        ->assertDispatched('post-created');
});

测试组件之间的事件通信:

php
it('updates post count when event is dispatched', function () {
    $badge = Livewire::test('post-count-badge')
        ->assertSee('0');

    Livewire::test('post.create')
        ->set('title', 'New post')
        ->call('save')
        ->assertDispatched('post-created');

    $badge->dispatch('post-created')
        ->assertSee('1');
});

断言事件使用特定参数分发:

php
it('dispatches notification when deleting post', function () {
    Livewire::test('post.show')
        ->call('delete', postId: 3)
        ->assertDispatched('notify', message: 'Post deleted');
});

对于复杂的断言,使用闭包:

php
it('dispatches event with correct data', function () {
    Livewire::test('post.show')
        ->call('delete', postId: 3)
        ->assertDispatched('notify', function ($event, $params) {
            return ($params['message'] ?? '') === 'Post deleted';
        });
});

使用 PHPUnit

虽然推荐使用 Pest,但你完全可以使用 PHPUnit 来测试 Livewire 组件。所有相同的测试工具都可以与 PHPUnit 的语法一起使用。

以下是一个 PHPUnit 示例供比较:

php
<?php

namespace Tests\Feature\Livewire;

use Livewire\Livewire;
use App\Models\Post;
use Tests\TestCase;

class CreatePostTest extends TestCase
{
    public function test_can_create_post()
    {
        $this->assertEquals(0, Post::count());

        Livewire::test('post.create')
            ->set('title', 'My new post')
            ->set('content', 'Post content')
            ->call('save');

        $this->assertEquals(1, Post::count());
    }

    public function test_title_is_required()
    {
        Livewire::test('post.create')
            ->set('title', '')
            ->call('save')
            ->assertHasErrors('title');
    }
}

本页面记录的所有功能都与 PHPUnit 完全相同——只需使用 PHPUnit 的断言语法代替 Pest 的。

考虑尝试 Pest

如果你有兴趣探索 Pest 更优雅的语法和功能,请查看 pestphp.com 了解更多。

所有可用的测试方法

以下是所有可用 Livewire 测试方法的综合参考:

设置方法

方法描述
Livewire::test('post.create')测试 post.create 组件
Livewire::test(UpdatePost::class, ['post' => $post])使用传递给 mount() 的参数测试 UpdatePost 组件
Livewire::actingAs($user)为测试设置已认证用户
Livewire::withQueryParams(['search' => '...'])设置 URL 查询参数(例如 ?search=...
Livewire::withCookie('name', 'value')为测试设置 cookie
Livewire::withCookies(['color' => 'blue', 'name' => 'Taylor'])设置多个 cookies
Livewire::withHeaders(['X-Header' => 'value'])设置自定义头
Livewire::withoutLazyLoading()在此测试中禁用所有组件的懒加载

与组件交互

方法描述
set('title', '...')title 属性设置为提供的值
set(['title' => '...', 'content' => '...'])使用数组设置多个属性
toggle('sortAsc')truefalse 之间切换布尔属性
call('save')调用 save 操作/方法
call('remove', $postId)使用参数调用方法
refresh()触发组件重新渲染
dispatch('post-created')从组件分发事件
dispatch('post-created', postId: $post->id)使用参数分发事件

断言

方法描述
assertSet('title', '...')断言属性等于提供的值
assertNotSet('title', '...')断言属性不等于提供的值
assertCount('posts', 3)断言属性包含 3 个项目
assertSee('...')断言渲染的 HTML 包含提供的文本
assertDontSee('...')断言渲染的 HTML 不包含提供的文本
assertSeeHtml('<div>...</div>')断言原始 HTML 出现在渲染输出中
assertDontSeeHtml('<div>...</div>')断言原始 HTML 不出现在渲染输出中
assertSeeInOrder(['first', 'second'])断言字符串按顺序出现在渲染输出中
assertDispatched('post-created')断言事件已被分发
assertNotDispatched('post-created')断言事件未被分发
assertHasErrors('title')断言属性的验证失败
assertHasErrors(['title' => ['required', 'min:6']])断言特定验证规则失败
assertHasNoErrors('title')断言属性没有验证错误
assertRedirect()断言触发了重定向
assertRedirect('/posts')断言重定向到特定 URL
assertRedirectToRoute('posts.index')断言重定向到命名路由
assertNoRedirect()断言没有触发重定向
assertViewHas('posts')断言数据已传递给视图
assertViewHas('postCount', 3)断言视图数据具有特定值
assertViewHas('posts', function ($posts) { ... })断言视图数据通过自定义验证
assertViewIs('livewire.show-posts')断言渲染了特定视图
assertFileDownloaded()断言触发了文件下载
assertFileDownloaded($filename)断言下载了特定文件
assertUnauthorized()断言抛出了授权异常 (401)
assertForbidden()断言访问被禁止 (403)
assertStatus(500)断言返回了特定状态码

另请参阅

  • 操作 — 测试组件操作和交互
  • 表单 — 测试表单提交和验证
  • 事件 — 测试事件分发和监听
  • 组件 — 创建可测试的组件结构