Laravel hasManyのUnitTest

– 想定する条件において、親子の各テーブルをassertCount, assertEquals、assertNotNullでcheckする
– feature testとunit testの違い
 -> userの挙動のテストがfeature test, unit testはuser操作が関係ない
– unit testのデータ生成にはfactoryを活用

$ php artisan make:test BookReservationsTest

namespace Tests\Unit;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Book;
use App\User;

class BookReservationsTest extends TestCase
{
    
    use RefreshDatabase;

    public function test_a_book_can_be_checked_out(){

        $book = factory(Book::class)->create();
        $user = factory(User::class)->create();

        $book->checkout($user);

        $this->assertCount(1, Reservation::all());
        $this->assertEquals($user->id, Reservation::first()->user_id);
        $this->assertEquals($book->id, Reservation::first()->book_id);
        $this->assertEquals(now(), Reservation::first()->checked_out_at);
    }
}

$ phpunit –filter test_a_book_can_be_checked_out
Tests\Unit\BookReservationsTest::test_a_book_can_be_checked_out

$ php artisan make:factory BookFactory -m Book
BookFactory.php

use App\Book;
use App\Author;
use Faker\Generator as Faker;

$factory->define(Book::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence,
        'author_id' => factory(Author::class),
    ];
});

$ phpunit –filter test_a_book_can_be_checked_out

$ php artisan make:factory AuthorFactory -m Author

use App\Author;
use Faker\Generator as Faker;

$factory->define(Author::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'dob' => now()->subYears(10),
    ];
});

Book.php

public function checkout(){
    	
    }

$ php artisan make:model Reservation -m

Book.php

public function checkout(User $user){
    	$this->reservations()->create([
    		'user_id' => $user->id,
    		'checked_out_at' => now(),
    	]);
    }

    public function reservations(){
    	return $this->hasMany(Reservation::class);	
    }

Reservation.php

protected $fillable = [
        'user_id',
        'book_id',
        'checked_out_at'
    ];

migration file

Schema::create('reservations', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->unsignedBigInteger('book_id');
            $table->timestamp('checked_out_at');
            $table->timestamp('checked_in_at');
            $table->timestamps();
        });

$ phpunit –filter test_a_book_can_be_checked_out

public function test_a_book_can_be_returned(){
        $book = factory(Book::class)->create();
        $user = factory(User::class)->create();

        $book->checkin();

        $this->assertCount(1, Reservation::all());
        $this->assertEquals($user->id, Reservation::first()->user_id);
        $this->assertEquals($book->id, Reservation::first()->book_id);
        $this->assertEquals(now(), Reservation::first()->checked_in_at);

    }

Book.php

public function checkin($user){
    	$reservation = $this->reservations()->where('user_id', $user->id)->whereNotNull('checked_out_at')->whereNull('checked_in_at')->first();

    	$reservation->update([
    		'checked_in_at' => now(),
    	]);
    }
public function test_a_book_can_be_returned(){
        $book = factory(Book::class)->create();
        $user = factory(User::class)->create();

        $book->checkout($user);
        $book->checkin($user);

        $this->assertCount(1, Reservation::all());
        $this->assertEquals($user->id, Reservation::first()->user_id);
        $this->assertEquals($book->id, Reservation::first()->book_id);
        $this->assertEquals(now(), Reservation::first()->checked_in_at);

    }

    public function test_a_user_can_checkout_a_book_twice(){

        $book = factory(Book::class)->create();
        $user = factory(User::class)->create();
        $book->checkout($user);
        $book->checkin($user);

        $book->checkout($user);

        $this->assertCount(2, Reservation::all());
        $this->assertEquals($user->id, Reservation::find(2)->user_id);
        $this->assertEquals($book->id, Reservation::find(2)->book_id);
        $this->assertNull(Reservation::find(2)->checked_in_at);
        $this->assertEquals(now(), Reservation::find(2)->checked_out_at);

        $book->checkin($user);

        $this->assertCount(2, Reservation::all());
        $this->assertEquals($user->id, Reservation::find(2)->user_id);
        $this->assertEquals($book->id, Reservation::find(2)->book_id);
        $this->assertNotNull(Reservation::find(2)->checked_in_at);
        $this->assertEquals(now(), Reservation::find(2)->checked_in_at);

    }

UnitTestを細かくやる人はこだわりがあり、レベルが高い印象があります。