PHPUnit Dasar — Testing

Apa itu PHPUnit? PHPUnit adalah framework testing standar untuk PHP. Di Laravel, PHPUnit sudah terinstal secara default dan siap digunakan. Struktur Test Class

PHPUnit adalah framework testing standar untuk PHP: kamu menulis method yang menjalankan kodemu, lalu menyatakan hasil yang kamu harapkan lewat assertion (pernyataan "harusnya begini"). Laravel sudah memasangnya sejak project dibuat. Bayangin petugas QC di pabrik biskuit: tiap biskuit dicocokkan dengan daftar "harusnya begini". Cocok, lanjut. Gak cocok, disisihkan dan dicatat alasannya. PHPUnit juga begitu: dia mencetak titik untuk tiap test yang cocok, huruf F untuk yang gagal, lalu menulis alasannya.

🧪 Tebakan cepat: $this->assertEquals(5, '5') dan $this->assertSame(5, '5'). Mana yang lulus? Tebak dulu, jawabannya ada di bagian "assertEquals atau assertSame?".

Test pertama: class, method, assert

Kode yang mau dites ada di app/Calculator.php:

<?php

namespace App;

class Calculator
{
    public static function add($a, $b)
    {
        return $a + $b;
    }
}

Test-nya di tests/Unit/CalculatorTest.php:

<?php

namespace Tests\Unit;

use App\Calculator;
use PHPUnit\Framework\TestCase;

class CalculatorTest extends TestCase
{
    public function test_add_dua_angka(): void
    {
        $this->assertEquals(5, Calculator::add(2, 3));
    }
}

Menjalankan dan membaca hasilnya

Jalankan dari folder project dengan ./vendor/bin/phpunit (di Laravel juga bisa php artisan test). Kalau semua cocok:

PHPUnit 12.5.14 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.6
Configuration: /home/budi/kalkulator/phpunit.xml

.                                                                   1 / 1 (100%)

Time: 00:00.002, Memory: 26.50 MB

OK (1 test, 1 assertion)

Kalau add keliru menyambung teks (return $a . $b;, hasilnya '23'):

PHPUnit 12.5.14 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.6
Configuration: /home/budi/kalkulator/phpunit.xml

F                                                                   1 / 1 (100%)

Time: 00:00.003, Memory: 26.50 MB

There was 1 failure:

1) Tests\Unit\CalculatorTest::test_add_dua_angka
Failed asserting that '23' matches expected 5.

/home/budi/kalkulator/tests/Unit/CalculatorTest.php:12

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertEquals atau assertSame?

Ini jawaban tebakannya: assertEquals(5, '5') lulus, assertSame(5, '5') gagal.

Bedanya kerasa di kasus nyata. round(2 + 3, 2) menghasilkan float 5.0. assertEquals(5, 5.0) lulus, tapi assertSame(5, 5.0) gagal dengan Failed asserting that 5.0 is identical to 5. Kalau tipe ikut penting, pakai assertSame.

Assertion lain yang sering dipakai

Assertion Lulus kalau Pesan kalau gagal
assertTrue($x) / assertFalse($x) $x persis true / false Failed asserting that false is true.
assertNull($x) $x adalah null
assertCount(3, $arr) isi array ada 3 Failed asserting that actual size 2 matches expected size 3.
assertContains('apel', $arr) array memuat 'apel' Failed asserting that an array contains 'apel'.
assertArrayHasKey('nama', $arr) array punya key nama
assertStringContainsString('Halo', $s) teks memuat Halo
assertGreaterThan(5, $x) $x lebih besar dari 5 Failed asserting that 3 is greater than 5.
assertInstanceOf(User::class, $u) $u objek User

setUp(): disiapkan ulang sebelum tiap test

Kalau beberapa test butuh objek yang sama, siapkan di setUp(). PHPUnit memanggilnya sebelum tiap test, jadi tiap test dapat objek baru yang bersih.

class KeranjangTest extends TestCase
{
    private Keranjang $keranjang;

    protected function setUp(): void
    {
        parent::setUp();
        $this->keranjang = new Keranjang();
    }

    protected function tearDown(): void
    {
        // bersihkan file atau koneksi kalau ada
        parent::tearDown();
    }
}

Urutan untuk dua test: setUp, test_satu, tearDown, lalu setUp, test_dua, tearDown. parent::setUp() jangan dibuang, karena TestCase (apalagi milik Laravel) menyiapkan banyak hal di situ.

Data provider: satu test, banyak data

Daripada menyalin test yang sama untuk tiap angka, pisahkan datanya ke method provider:

use PHPUnit\Framework\Attributes\DataProvider;

public static function angkaProvider(): array
{
    return [
        'dua' => [2, true],
        'empat' => [4, false],
        'tujuh' => [7, true],
        'sembilan' => [9, false],
    ];
}

#[DataProvider('angkaProvider')]
public function test_is_prime(int $angka, bool $harusnya): void
{
    $this->assertSame($harusnya, Calculator::isPrime($angka));
}

Menguji exception

public function test_bagi_nol_ditolak(): void
{
    $this->expectException(InvalidArgumentException::class);
    Calculator::bagi(10, 0);
}

expectException ditulis sebelum kode yang melempar. Test lulus kalau exception itu benar-benar dilempar. Kalau gak ada yang dilempar: Failed asserting that exception of type "InvalidArgumentException" is thrown.

Di latihan: tiru assertion-nya sendiri

Sandbox latihan menjalankan PHP biasa, tanpa PHPUnit. Jadi kamu menulis pemeriksaannya sendiri dan mencetak hasilnya:

function assertEquals($expected, $actual): string
{
    return $expected === $actual ? "PASS" : "FAIL";
}

echo "1) " . assertEquals(5, 2 + 3) . "\n";   // 1) PASS
echo "3) " . assertEquals(10, 3 * 3) . "\n";  // 3) FAIL

⚠️ Jebakan yang sering ketemu

TL;DR: Test PHPUnit adalah class ...Test extends TestCase dengan method test_...(): void yang memanggil $this->assert...(diharapkan, sebenarnya). Jalankan dengan ./vendor/bin/phpunit: . lulus, F gagal, E error. assertEquals membandingkan longgar (==), assertSame ketat (===, tipe ikut dicek). Ada juga assertTrue, assertFalse, assertCount, assertContains. setUp() jalan sebelum tiap test. #[DataProvider('namaProvider')] menjalankan satu test untuk tiap kasus dari provider public static. expectException(InvalidArgumentException::class) ditulis sebelum kode yang melempar. Di sandbox latihan, tiru assertion-nya dengan === lalu echo PASS atau FAIL.

Yang akan kamu pelajari