Mena
Mena

Reputation: 2029

Calling factories within another factory

I am currently learning how to write PHPUnit test using a laravel app and hit a brick wall for some days now. My test was to allow only authenticated users update a book record if the user's id is the same as the book's user's id.

public function test_onlyAuthenticatedUserCanUpdateBookSuccessfully()
{
    $user = factory(User::class)->create();
    Passport::actingAs($user);

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

    dd($user, $book);

    // $response = $this->json('PUT', '/api/books/'.$book->id, [
    //         'id'    => $book->id,
    //         'title' => 'Updated book title',
    //         'author'=> 'New Guy'
    //     ]);

    // $response->assertStatus(201);
}

But I got a 403 error initially and while troubleshooting, i noticed from the dumped data that the created user's id is not the same as the book's user_id.

// Newly created user
#original: array:7 [
    "name" => "Prof. Norwood Erdman"
    "email" => "[email protected]"
    "updated_at" => "2018-10-03 14:11:20"
    "created_at" => "2018-10-03 14:11:20"
    "id" => 2
]

// Newly created book
#attributes: array:6 [
    "title" => "Quasi laudantium enim quas omnis."
    "author" => "Mr. Jayson Roob"
    "user_id" => 1
    "updated_at" => "2018-10-03 14:11:20"
    "created_at" => "2018-10-03 14:11:20"
    "id" => 3
]

Going through my code, I discovered that every time a book is created the user_id will be set to 1 because my BooksFactory is using a hard coded value of user_id = 1.

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

How can I write my code so that in my test when a book is created, the authenticated user's id is assigned as the book's user's id? I think that will fix the test. I am using sqlite memory database for test and not mysql.

Upvotes: 3

Views: 5256

Answers (2)

Rwd
Rwd

Reputation: 35170

You can override attributes in a factory by passing an array to the create() method with the attributes and values that you want to override:

$book = factory(Book::class)->create(['user_id' => $user->id]);

Furthermore, it might be a good idea to create create the user in the factory itself i.e.:

$factory->define(Book::class, function (Faker $faker) {
    return [
        'title'     => $faker->sentence,
        'author'    => $faker->name,
        'user_id'   => function () {
            return factory(App\User::class)->create()->id;
        }
    ];
});

The reason the user_id value in wrapped in a closure is to stop another user being created if you want to pass in your own id.

Overriding Attributes Documentation

Upvotes: 9

ConorJohn
ConorJohn

Reputation: 667

As a small update, for Laravel 8, the syntax would be:

$factory->define(Book::class, function (Faker $faker) {
    return [
        'title'     => $faker->sentence,
        'author'    => $faker->name,
        'user_id'   => User::factory()->create()->id
    ];
});

Upvotes: 1

Related Questions