Di Laravel, ada beberapa cara untuk bekerja dengan pabrik model (model factories) dalam pengujian fitur. Misalnya, Anda bisa membuat model di fungsi setUp()
jika ingin menggunakannya untuk beberapa pengujian atau langsung membuatnya di tiap kasus pengujian.
Namun, bagaimana jika Anda ingin menguji kasus dengan berbagai macam data? Di sinilah data provider PHPUnit dengan model Eloquent berperan.
Menggunakan data provider pada pengujian fitur bisa jadi merepotkan karena ia dijalankan sebelum Laravel di-bootstrapping melalui class TestCase
yang dijalankan selama setUp()
. Data provider dieksekusi di awal proses menjalankan phpunit
, sehingga Anda akan menemui error berikut jika ingin menggunakannya:
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
#[DataProvider('nonAdminUsers')]
public function test_non_admin_users_cannot_access_admin($user): void
{
$response = $this
->actingAs($user())
->get('/admin')
->assertStatus(403);
}
public static function nonAdminUsers(): array
{
return [
[User::factory()->player()->create()],
[User::factory()->coach()->create()],
[User::factory()->owner()->create()],
];
}
}
Jika Anda menjalankan pengujian di atas, Anda akan mendapatkan error seperti berikut, tergantung versi Laravel Anda (contoh ini untuk Laravel 11):
$ vendor/bin/phpunit tests/Feature/ExampleTest.php
There was 1 PHPUnit error:
1) Tests\Feature\ExampleTest::test_non_admin_users_cannot_access_admin
The data provider specified for Tests\Feature\ExampleTest::test_non_admin_users_cannot_access_admin is invalid
A facade root has not been set.
tests/Feature/ExampleTest.php:18
Ini terjadi karena ketika kode data provider dijalankan, aplikasi Laravel belum di-bootstrapping!
Solusi Menggunakan Closure
Jika Anda pengguna Pest PHP, contoh "Bound Datasets" menggunakan closure untuk data model:
it('can generate the full name of a user', function (User $user) {
expect($user->full_name)->toBe("{$user->first_name} {$user->last_name}");
})->with([
fn() => User::factory()->create(['first_name' => 'Nuno', 'last_name' => 'Maduro']),
fn() => User::factory()->create(['first_name' => 'Luke', 'last_name' => 'Downing']),
fn() => User::factory()->create(['first_name' => 'Freek', 'last_name' => 'Van Der Herten']),
]);
Di PHPUnit, kita bisa menggunakan closure untuk mengirimkan kode ke pengujian melalui data provider tanpa langsung mencoba membuat datanya:
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
#[DataProvider('nonAdminUsers')]
public function test_non_admin_users_cannot_access_admin($user): void
{
$response = $this
->actingAs($user())
->get('/admin')
->assertStatus(403);
}
public static function nonAdminUsers(): array
{
return [
[fn(): User => User::factory()->player()->create()],
[fn(): User => User::factory()->coach()->create()],
[fn(): User => User::factory()->owner()->create()],
];
}
}
Perhatikan pemanggilan $user()
, yang kita teruskan ke actingAs()
. Jika Anda perlu menggunakan model di berbagai tempat, tetapkan saja ke sebuah variabel. Sekarang, data pabrik dibuat di dalam pengujian, sesuai dengan yang kita inginkan!
Pelajari Lebih Lanjut
Untuk mempelajari lebih lanjut tentang pengujian fitur HTTP di Laravel, cek dokumentasi resminya.
Kesimpulan
Artikel ini menjelaskan bagaimana mengatasi error yang muncul ketika menggunakan data provider PHPUnit dengan pabrik model Eloquent dalam pengujian fitur Laravel. Solusi yang disarankan adalah menggunakan closure untuk menunda pembuatan data pabrik hingga ke dalam pengujian itu sendiri. Ini memastikan bahwa aplikasi