Good content takes time and effort to come up with.

Please consider supporting us by just disabling your AD BLOCKER and reloading this page again.







Factory States For Clean And Fluent Laravel Testing | StackCoder


Factory States For Clean And Fluent Laravel Testing


06th September 2020 4 mins read
Share On     Share On WhatsApp     Share On LinkedIn


In my previous article, I had explained why should we use Laravel factories. You can check the article at Factories To Speed Up Test-Driven Development In Laravel.


In this article let's see how we can use Factory states to write fluent test cases.


Without Factory States


First, let's see how does our test cases look like without any factory states. Basically, we are writing a test to check that the user cannot view any unpublished blog posts.


In Laravel 8


tests / Feature / PostsTest.php

/**
* @test
*/
public function user_cannot_view_unpublished_post()
{
    $post = Post::factory()->create([
        'published_at'  => null
    ]);
    $resource = $this->get("/posts/" . $post->slug);

    $resource->assertStatus(404);
}


database / factories / PostFactory.php

public function definition() {
    $title = $this->faker->title;

    return [
        'title'         => $title,
        'slug'          => Str::slug($title),
        'summary'       => $this->faker->paragraph,
        'body'          => $this->faker->paragraph,
        'author_id'     => function () {
            return User::factory()->create()->id;
        },
        'published_at'  => Carbon::parse('-1 week'),
    ];
}

Laravel 7 & Earlier Versions


tests / Feature / PostsTest.php

/**
* @test
*/
public function user_cannot_view_unpublished_post()
{
    $post = factory(Post::class)->create([
        'published_at'  => null
    ]);
    $resource = $this->get("/posts/" . $post->slug);

    $resource->assertStatus(404);
}


database / factories / PostFactory.php

$factory->define(Post::class, function (Faker $faker) {
    $title = $faker->title;
    return [
        'title'         => $title,
        'slug'          => Str::slug($title),
        'summary'       => $faker->paragraph,
        'body'          => $faker->paragraph,
        'author_id'     => function () {
            return factory(User::class)->create()->id;
        },
    ];
});

With Factory States


Basically, there is no problem with the above approach. Since we can write more cleaner we can use this approach.


From the above code snippet, we can write the code


Laravel 8


tests / Feature / PostsTest.php

/**
* @test
*/
public function user_cannot_view_unpublished_post()
{
  $post = Post::factory()->states(['unpublished'])->create();
  $resource = $this->get("/posts/" . $post->slug);
  $resource->assertStatus(404);
}


database / factories / PostFactory.php


use App\Post;
use App\User;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Faker\Generator as Faker;

public function definition(){
  $title = $this->faker->title;
  return [
    'title'     => $title,
    'slug'     => Str::slug($title),
    'summary'    => $this->faker->paragraph,
    'body'     => $this->faker->paragraph,
    'author_id'   => function () {
      return User::factory()->create()->id;
    },
    'published_at' => Carbon::parse('-1 week'),
  ];
}


public function unpublished()
{
  return $this->state(function (array $attributes) {
    return [
      'published_at' => null
    ];
  });
}

public function published()
{
  return $this->state(function (array $attributes) {
    return [
      'published_at' => Carbon::parse('-1 week')
    ];
  });
}



Laravel 7 & Earlier Versions


From

$post = factory(Post::class)->create([
    'published_at'  => null
]);

To

$post = factory(Post::class)->states(['unpublished'])->create();

Now your test case will look like the following very lean, clean & fluent


Laravel 7 & Earlier Versions


tests / Feature / PostsTest.php

/**
* @test
*/
public function user_cannot_view_unpublished_post()
{
    $post = factory(Post::class)->states(['unpublished'])->create();
    $resource = $this->get("/posts/" . $post->slug);
    $resource->assertStatus(404);
}


database / factories / PostFactory.php

use App\Post;
use App\User;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Faker\Generator as Faker;

$factory->define(Post::class, function (Faker $faker) {
    return [
        'title'         => $title,
        'slug'          => Str::slug($title),
        'summary'       => $faker->paragraph,
        'body'          => $faker->paragraph,
        'author_id'     => function () {
            return factory(User::class)->create()->id;
        },
    ];
});

$factory->state(Post::class, 'unpublished', function (Faker $faker) {
    return [
        'published_at' => null
    ];
});

/** Similarly you can have published state too */

$factory->state(Post::class, 'published', function (Faker $faker) {
    return [
        'published_at' => Carbon::parse('-1 week')
    ];
});

Conclusion


I hope this article helped you. Please share it with your friends.




Author Image
AUTHOR

Channaveer Hakari

I am a full-stack developer working at WifiDabba India Pvt Ltd. I started this blog so that I can share my knowledge and enhance my skills with constant learning.

Never stop learning. If you stop learning, you stop growing