Belajar PHP - Testing (PHPUnit/Pest) & Debug
Series/Belajar PHP/Episode 10
Episode 10 of 23

Belajar PHP - Testing (PHPUnit/Pest) & Debug

Memastikan kualitas kode PHP: menulis unit test dengan PHPUnit dan Pest, memahami assertions, test structure, data provider, dan coverage, lalu debugging dengan Xdebug, var_dump/dd(), dan static analysis dengan PHPStan/Psalm sebagai jaring pengaman sebelum produksi.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sampai episode 9, kita memverifikasi kode dengan cara manual: jalankan, lihat output, perbaiki. Itu tidak akan bertahan di proyek nyata. Episode ini mengajarkan kalian testing otomatis — cara menulis kode yang menguji kode — plus debugging untuk saat semuanya salah. Dua keterampilan yang paling membedakan junior dari senior.

Mengapa penting? Sebuah aplikasi produksi punya ratusan fungsi. Mengubah satu baris bisa merusak sepuluh hal lain yang tidak kalian ingat. Test otomatis menangkap regresi itu dalam hitungan detik, sementara static analysis menangkap kelas bug yang belum sempat dijalankan. Di episode 13-22, setiap kode yang kita tulis akan selalu disertai test.

PHPUnit: Test Framework Klasik

Install dan siapkan:

Install PHPUnit sebagai dev dependency
composer require --dev phpunit/phpunit

Konfigurasi di phpunit.xml:

phpunit.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php" colors="true">
    <testsuites>
        <testsuite name="Unit">
            <directory>tests/Unit</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory>src</directory>
        </include>
    </source>
</phpunit>

Fungsi yang akan diuji:

src/Price.php
namespace App;
 
class Price
{
    public static function withTax(float $amount, float $rate = 0.11): float
    {
        return round($amount * (1 + $rate), 2);
    }
}

Unit test pertama:

tests/Unit/PriceTest.php
namespace App\Tests;
 
use PHPUnit\Framework\TestCase;
use App\Price;
 
final class PriceTest extends TestCase
{
    public function testTaxIsAdded(): void
    {
        self::assertSame(111.00, Price::withTax(100.00));
    }
 
    public function testTaxRateIsConfigurable(): void
    {
        self::assertSame(105.00, Price::withTax(100.00, 0.05));
    }
}

Jalankan:

Jalankan PHPUnit
vendor/bin/phpunit

Data Provider dan Coverage

Gunakan data provider untuk menguji banyak kasus dengan satu test, dan hitung coverage:

Data provider
use PHPUnit\Framework\Attributes\DataProvider;
 
final class PriceTest extends TestCase
{
    #[DataProvider('taxProvider')]
    public function testTaxForVariousAmounts(float $amount, float $expected): void
    {
        self::assertSame($expected, Price::withTax($amount));
    }
 
    public static function taxProvider(): array
    {
        return [
            "nol"      => [0.00, 0.00],
            "kecil"    => [1.00, 1.11],
            "ratusan"  => [100.00, 111.00],
            "negatif?" => [-50.00, -55.50],
        ];
    }
}
Cek coverage
vendor/bin/phpunit --coverage-text

Coverage 100% bukan tujuan akhir, tapi angka di bawah 80% untuk logika inti (harga, hak akses, validasi) adalah sinyal bahaya.

Pest: Test Modern yang Fluent

Pest dibangun di atas PHPUnit dengan sintaks yang lebih ekspresif:

Install Pest
composer require --dev pestphp/pest --with-all-dependencies
vendor/bin/pest --init

Test Pest untuk fungsi yang sama:

tests/Unit/PriceTest.php (Pest)
use App\Price;
 
it("menambahkan pajak 11%", function () {
    expect(Price::withTax(100.00))->toBe(111.00);
});
 
it("menghormati rate pajak custom", function (float $rate, float $expected) {
    expect(Price::withTax(100.00, $rate))->toBe($expected);
})->with([
    [0.05, 105.00],
    [0.25, 125.00],
]);

Pest unggul di readability — test dibaca seperti kalimat. Di episode 13+ kita akan memakai Pest karena sintaksnya pas untuk test Laravel/Slim.

Tip

Pilih satu: Pest untuk proyek baru (lebih ekspresif), PHPUnit untuk proyek lama (paling banyak dipakai di industri). Keduanya saling kompatibel — file test PHPUnit bisa berjalan di dalam Pest. Yang penting konsisten dalam satu proyek.

Debug: var_dump, dd(), dan Logging

Debugging paling sederhana:

var_dump dan dump-and-die
var_dump($user);              // cetak semua properti + tipe
print_r($user, true);          // representasi lebih ringkas
 
// Laravel/TallStack: dd() = dump + die
// dd($request->all());

Aturan praktis debugging:

  1. Reproduksi dulu — apa input yang membuat bug muncul.
  2. Dump di titik masuk data — sebelum fungsi, bukan setelahnya.
  3. Jangan tinggalkan var_dump di produksi — ganti dengan logging (error_log atau PSR-3 logger).

Xdebug: Step Debugger

var_dump cukup untuk masalah kecil. Untuk bug logika yang sulit, pakai Xdebug sebagai debugger interaktif (breakpoint, step over/into, inspeksi variabel live):

Konfigurasi Xdebug di php.ini
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes

Dengan ekstensi PHP Debug di VS Code, kalian bisa menempatkan breakpoint di editor, lalu menekan Run and Debug — persis seperti debugging di bahasa lain. Xdebug juga bisa dipakai untuk profiling (episode 18) dengan xdebug.mode=profile.

Static Analysis: PHPStan dan Psalm

Static analysis membaca seluruh kode tanpa menjalankannya — menangkap bug potensial lebih awal:

Install dan jalankan PHPStan
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse src --level=8

Level 0-9 (PHPStan) atau tingkat serupa (Psalm). Semakin tinggi level, semakin ketat. Contoh yang dideteksi:

Bug yang ditangkap static analysis
function ambilNama(array $user): string
{
    return $user["nama"];   // PHPStan: offset "nama" mungkin tidak ada
}

PHPStan akan meminta kalian menegaskan atau memvalidasi aksesnya — mencegah Undefined array key di produksi.

Warning

Static analysis bukan pengganti test — dan test bukan pengganti static analysis. Gabungan ketiganya (test + static analysis + code review) adalah standar industri. Di episode 19 kita menggabungkan semuanya ke pipeline CI.

Common Pitfalls

  • Test tanpa assertionassertSame/expect()->toBe wajib; test kosong hanya beri rasa aman palsu.
  • Menguji implementasi, bukan perilaku — jangan test detail internal; test output dan kontraknya.
  • var_dump tertinggal di kode — merusak output HTTP/JSON; gunakan logger di produksi.
  • PHPStan level rendah — level 1-3 hampir tidak menangkap apa pun; target minimal level 5-6, ideal 8-9 untuk logika kritis.

Penutup

Inti yang harus dibawa pulang:

  • PHPUnit untuk test klasik, Pest untuk sintaks modern yang ekspresif.
  • Gunakan data provider dan ukur coverage untuk logika inti.
  • Debug sederhana dengan var_dump/dd(), bug sulit dengan Xdebug step debugger.
  • PHPStan/Psalm menangkap bug sebelum kode dijalankan — pasang minimal level 6.

Di episode 11 selanjutnya kita menghubungkan PHP ke dunia data: Working with Databases (PDO & Eloquent) — koneksi MySQL/PostgreSQL/SQLite, prepared statements yang aman dari SQL injection, transaksi, lalu ORM Eloquent dan Doctrine untuk produktivitas tinggi. Ini gerbang aplikasi nyata pertama kalian.

Belajar PHP - Testing (PHPUnit/Pest) & Debug | Belajar PHP