0

HTTP tests — $this->get(), post(), assertStatus(), assertJson()

Beginner5 min read·lv-26-003

Concept

Model Factories generate realistic test data. They define default attribute values, states, sequences, relationships, and afterCreating callbacks. Factories eliminate repetitive test setup code and make tests readable.

Creating: php artisan make:factory PostFactory --model=Post. Generates database/factories/PostFactory.php.

definition() method: Returns default attributes array. Uses Faker via $this->faker.

States: Method that modifies the factory state. Named with the semantic meaning:

php
public function published(): static
{
    return $this->state(['status' => 'published', 'published_at' => now()]);
}

for() method: Associate a belongs-to relationship. Post::factory()->for($user) sets user_id. Post::factory()->for(User::factory()) also creates the user.

has() method: Create has-many relationships. User::factory()->has(Post::factory()->count(3)) creates user with 3 posts.

Magic methods: Post::factory()->hasPosts(3) works for hasXxx() — calls the has() method for the posts relationship.

afterCreating(Closure): Run code after the model is created. Useful for setting up pivot data, dispatching events, etc.

sequence(array ...$sequences): Alternate between values: Post::factory()->count(3)->sequence(['status' => 'draft'], ['status' => 'published']).

Faker integration: $this->faker->name(), $this->faker->email(), $this->faker->paragraph(), $this->faker->numberBetween(1, 100), etc.

Code Example

php
<?php
namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class PostFactory extends Factory
{
    public function definition(): array
    {
        $title = $this->faker->sentence(rand(4, 8));
        return [
            'user_id'      => User::factory(),         // auto-creates a user
            'title'        => $title,
            'slug'         => Str::slug($title),
            'body'         => $this->faker->paragraphs(5, true),
            'status'       => 'draft',
            'published_at' => null,
            'view_count'   => 0,
        ];
    }

    // States
    public function published(): static
    {
        return $this->state([
            'status'       => 'published',
            'published_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
        ]);
    }

    public function trashed(): static
    {
        return $this->state(['deleted_at' => now()]);
    }

    public function popular(): static
    {
        return $this->state(['view_count' => $this->faker->numberBetween(1000, 50000)]);
    }
}

// Usage in tests
$draftPost     = Post::factory()->create();
$publishedPost = Post::factory()->published()->create();
$popularPost   = Post::factory()->published()->popular()->create();

// Sequences — alternate status
$posts = Post::factory()
    ->count(6)
    ->sequence(
        ['status' => 'draft'],
        ['status' => 'published'],
        ['status' => 'archived']
    )
    ->create();
// 6 posts: draft, published, archived, draft, published, archived

// Relationships
$author    = User::factory()->create();
$userPosts = Post::factory()->count(3)->for($author)->published()->create();

// has() — user with 5 posts
$userWithPosts = User::factory()
    ->has(Post::factory()->count(5)->published())
    ->create();