加入收藏 | 设为首页 | 会员中心 | 我要投稿 聊城站长网 (https://www.0635zz.com/)- 智能语音交互、行业智能、AI应用、云计算、5G!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

怎样使用Laravel模型工厂,示例是怎样的?

发布时间:2023-05-08 13:57:05 所属栏目:PHP教程 来源:
导读:这篇我们来学习和了解“怎样使用Laravel模型工厂,实例是怎样的?”,下文的讲解详细,步骤过程清晰,对大家进一步学习和理解“怎样使用Laravel模型工厂,实例是怎样的?”有一定的帮助。有这方
这篇我们来学习和了解“怎样使用Laravel模型工厂,实例是怎样的?”,下文的讲解详细,步骤过程清晰,对大家进一步学习和理解“怎样使用Laravel模型工厂,实例是怎样的?”有一定的帮助。有这方面学习需要的朋友就继续往下看吧!
 
Laravel 模型工厂是你可以在应用程序中进行测试时使用的最佳功能之一。它们提供了一种定义可预测且易于复制的数据的方法,以便你的测试保持一致和可控。
 
让我们从一个简单的例子开始。我们有一个用于写博客的应用程序,所以很自然地,我们有一个 Post 模型,该模型具有发布、起草或排队的状态。让我们看一下这个例子的 Eloquent 模型:
 
declare(strict_types=1);
 
namespace App\Models;
 
use App\Publishing\Enums\PostStatus;
 
use Illuminate\Database\Model;
 
class Post extends Model
 
{
 
    protected $fillable = [
 
        'title',
 
        'slug',
 
        'content',
 
        'status',
 
        'published_at',
 
    ];
 
    protected $casts = [
 
        'status' => PostStatus::class,
 
        'published_at' => 'datetime',
 
    ];
 
}登录后复制
 
正如你在此处看到的,我们有一个用于状态列的 Enum,我们现在将对其进行设计。在这里使用枚举允许我们利用 PHP 8.1 的特性,而不是纯字符串、布尔标志或混乱的数据库枚举。
 
 declare(strict_types=1);
 
namespace App\Publishing\Enums;
 
enum PostStatus: string
 
{
 
    case PUBLISHED = 'published';
 
    case DRAFT = 'draft';
 
    case QUEUED = 'queued';
 
}登录后复制
 
现在,让我们回到我们在这里讨论的主题:模型工厂。一个简单的工厂看起来很简单:
 
 declare(strict_types=1);
 
namespace Database\Factories;
 
use App\Models\Post;
 
use App\Publishing\Enums\PostStatus;
 
use Illuminate\Database\Eloquent\Factories\Factory;
 
use Illuminate\Support\Arr;
 
use Illuminate\Support\Str;
 
class PostFactory extends Factory
 
{
 
    protected $model = Post::class;
 
    public function definition(): array
 
    {
 
        $title = $this->faker->sentence();
 
        $status = Arr::random(PostStatus::cases());
 
        return [
 
            'title' => $title,
 
            'slug' => Str::slug($title),
 
            'content' => $this->faker->paragraph(),
 
            'status' => $status->value,
 
            'published_at' => $status === PostStatus::PUBLISHED
 
                ? now()
 
                : null,
 
        ];
 
    }
 
}登录后复制
 
所以在我们的测试中,我们现在可以快速调用我们的 post factory 为我们创建一个 post。让我们看看我们可以如何做到这一点:
 
 it('can update a post', function () {
 
    $post = Post::factory()->create();
 
    putJson(
 
        route('api.posts.update', $post->slug),
 
        ['content' => 'test content',
 
    )->assertSuccessful();
 
    expect(
 
        $post->refresh()
 
    )->content->toEqual('test content');
 
});登录后复制 登录后复制
 
一个足够简单的测试,但是如果我们的业务规则规定你只能根据帖子类型更新特定列,会发生什么?让我们重构我们的测试以确保我们可以做到这一点:
 
it('can update a post', function () {
 
    $post = Post::factory()->create([
 
        'type' => PostStatus::DRAFT->value,
 
    ]);
 
    putJson(
 
        route('api.posts.update', $post->slug),
 
        ['content' => 'test content',
 
    )->assertSuccessful();
 
    expect(
 
        $post->refresh()
 
    )->content->toEqual('test content');
 
});登录后复制
 
完美,我们可以将一个参数传递给 create 方法,以确保我们在创建它时设置正确的类型,这样我们的业务规则就不会抱怨。但是这样写有点麻烦,所以让我们稍微重构一下我们的工厂,添加修改状态的方法:
 
 declare(strict_types=1);
 
namespace Database\Factories;
 
use App\Models\Post;
 
use App\Publishing\Enums\PostStatus;
 
use Illuminate\Database\Eloquent\Factories\Factory;
 
use Illuminate\Support\Str;
 
class PostFactory extends Factory
 
{
 
    protected $model = Post::class;
 
    public function definition(): array
 
    {
 
        $title = $this->faker->sentence();
 
        return [
 
            'title' => $title,
 
            'slug' => Str::slug($title),
 
            'content' => $this->faker->paragraph(),
 
            'status' => PostStatus::DRAFT->value,
 
            'published_at' => null,
 
        ];
 
    }
 
    public function published(): static
 
    {
 
        return $this->state(
 
            fn (array $attributes): array => [
 
                'status' => PostStatus::PUBLISHED->value,
 
                'published_at' => now(),
 
            ],
 
        );
 
    }
 
}登录后复制
 
我们为工厂设置了默认值,以便所有新创建的帖子都是草稿。然后我们添加一个设置要发布的状态的方法,它将使用正确的 Enum 值并设置发布日期 - 在测试环境中更具可预测性和可重复性。让我们看看我们的测试现在是什么样子:
 
 it('can update a post', function () {
 
    $post = Post::factory()->create();
 
    putJson(
 
        route('api.posts.update', $post->slug),
 
        ['content' => 'test content',
 
    )->assertSuccessful();
 
    expect(
 
        $post->refresh()
 
    )->content->toEqual('test content');
 
});登录后复制 登录后复制
 
回到一个简单的测试——所以如果我们有多个测试想要创建一个草稿帖子,他们可以使用工厂。现在让我们为发布的状态编写一个测试,看看是否有错误。
 
 it('returns an error when trying to update a published post', function () {
 
    $post = Post::factory()->published()->create();
 
    putJson(
 
        route('api.posts.update', $post->slug),
 
        ['content' => 'test content',
 
    )->assertStatus(Http::UNPROCESSABLE_ENTITY());
 
    expect(
 
        $post->refresh()
 
    )->content->toEqual($post->content);
 
});登录后复制
 
这次我们正在测试当我们尝试更新已发布的帖子时是否收到验证错误状态。这可确保我们保护我们的内容并在我们的应用程序中强制执行特定的工作流程。
 
那么如果我们还想确保工厂中的特定内容会发生什么呢?我们可以根据需要添加另一种方法来修改状态:
 
 declare(strict_types=1);
 
namespace Database\Factories;
 
use App\Models\Post;
 
use App\Publishing\Enums\PostStatus;
 
use Illuminate\Database\Eloquent\Factories\Factory;
 
use Illuminate\Support\Str;
 
class PostFactory extends Factory
 
{
 
    protected $model = Post::class;
 
    public function definition(): array
 
    {
 
        return [
 
            'title' => $title = $this->faker->sentence(),
 
            'slug' => Str::slug($title),
 
            'content' => $this->faker->paragraph(),
 
            'status' => PostStatus::DRAFT->value,
 
            'published_at' => null,
 
        ];
 
    }
 
    public function published(): static
 
    {
 
        return $this->state(
 
            fn (array $attributes): array => [
 
                'status' => PostStatus::PUBLISHED->value,
 
                'published_at' => now(),
 
            ],
 
        );
 
    }
 
    public function title(string $title): static
 
    {
 
        return $this->state(
 
            fn (array $attributes): array => [
 
                'title' => $title,
 
                'slug' => Str::slug($title),
 
            ],
 
        );
 
    }
 
}登录后复制
 
因此,在我们的测试中,我们可以创建一个新测试,以确保我们可以通过我们的 API 更新草稿帖子标题:
 
 it('can update a draft posts title', function () {
 
    $post = Post::factory()->title('test')->create();
 
    putJson(
 
        route('api.posts.update', $post->slug),
 
        ['title' => 'new title',
 
    )->assertSuccessful();
 
    expect(
 
        $post->refresh()
 
    )->title->toEqual('new title')->slug->toEqual('new-title');
 
});登录后复制
 
所以我们可以很好地使用工厂状态来控制我们的测试环境中的东西,给我们尽可能多的控制权。这样做将确保我们始终如一地准备测试,或者很好地反映特定点的应用程序状态。
 
如果我们需要为我们的测试创建许多模型,我们该怎么办?我们应该怎么做?简单的答案是告诉工厂:
 
it('lists all posts', function () {
 
    Post::factory(12)->create();
 
    getJson(
 
        route('api.posts.index'),
 
    )->assertOk()->assertJson(fn (AssertableJson $json) =>
 
        $json->has(12)->etc(),
 
    );
 
});登录后复制
 
所以我们正在创建 12 个新帖子,并确保当我们获得索引路由时,我们有 12 个帖子返回。除了将 count 传递给工厂方法,你还可以使用 count 方法:
 
Post::factory()->count(12)->create();登录后复制
 
但是,在我们的应用程序中,有时我们可能希望以特定顺序运行事物。假设我们希望第一个是草稿,但第二个已发布?
 
 it('shows the correct status for the posts', function () {
 
    Post::factory()
 
        ->count(2)
 
        ->state(new Sequence(
 
            ['status' => PostStatus::DRAFT->value],
 
            ['status' => PostStatus::PUBLISHED->value],
 
        ))->create();
 
    getJson(
 
        route('api.posts.index'),
 
    )->assertOk()->assertJson(fn (AssertableJson $json) =>
 
        $json->where('id', 1)
 
            ->where('status' PostStatus::DRAFT->value)
 
            ->etc();
 
    )->assertJson(fn (AssertableJson $json) =>
 
        $json->where('id', 2)
 
            ->where('status' PostStatus::PUBLISHED->value)
 
            ->etc();
 
    );
 
});登录后复制
 
 

(编辑:聊城站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!