Belajar CodeIgniter - Testing (PHPUnit)
Episode 17 of 27

Belajar CodeIgniter - Testing (PHPUnit)

Membangun kepercayaan pada kode dengan PHPUnit: setup test environment, menulis unit test dan feature test, menguji interaksi database, serta praktik mengetes controller dan model secara nyata.

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

Pendahuluan

Sejauh ini kalian menguji aplikasi dengan membuka browser dan klik manual. Masalahnya: manual testing tidak konsisten, melelahkan, dan mudah melewatkan regresi. Di episode 17 ini kita masuk ke disiplin yang mengubah cara kerja kalian: automated testing dengan PHPUnit.

Mengapa penting? Karena aplikasi akan terus berubah — menambah fitur, refactor, upgrade library — dan setiap perubahan berpotensi memecahkan yang sudah jalan. Test adalah jaring pengaman yang menjerit sebelum regresi sampai ke produksi. CodeIgniter 4 datang dengan integrasi PHPUnit yang siap pakai.

Setup Test Environment

Template appstarter sudah menyertakan phpunit.xml.dist dan folder tests/. Pastikan dependency test terinstall:

Install dependency test
composer require --dev phpunit/phpunit

Cek file phpunit.xml.dist — ia mendefinisikan environment testing, termasuk database test:

phpunit.xml.dist
<phpunit bootstrap="vendor/codeigniter4/framework/system/Test/bootstrap.php">
    <testsuites>
        <testsuite name="app">
            <directory>./tests</directory>
        </testsuite>
    </testsuites>
 
    <php>
        <const name="HOMEPATH" value="./"/>
        <const name="CONFIGPATH" value="./app/Config/"/>
        <const name="PUBLICPATH" value="./public/"/>
        <env name="CI_ENVIRONMENT" value="testing"/>
        <env name="database.tests.default.hostname" value="localhost"/>
        <env name="database.tests.default.database" value="ci_blog_test"/>
    </php>
</phpunit>

Perhatikan CI_ENVIRONMENT = testing dan database group tests yang terpisah — test tidak boleh menimpa database development/produksi kalian.

Jalankan test
php spark test

php spark test adalah wrapper CodeIgniter untuk PHPUnit dengan konfigurasi yang sudah dimuat.

Warning

Jangan pernah menjalankan test terhadap database produksi. Sediakan database khusus test (misal ci_blog_test) dan set via database.tests.* di phpunit.xml.dist — test akan membuat/menghapus data tanpa ampun.

Unit Test

Unit test menguji satu unit kode secara terisolasi — biasanya method class. Contoh: test library Slugger dari episode 10.

tests/unit/SluggerTest.php
<?php
 
namespace Tests\Unit;
 
use App\Libraries\Slugger;
use CodeIgniter\Test\CIUnitTestCase;
 
final class SluggerTest extends CIUnitTestCase
{
    public function testMakeMengubahJudulMenjadiSlug(): void
    {
        $slugger = new Slugger();
 
        $this->assertSame(
            'belajar-codeigniter-4',
            $slugger->make('Belajar CodeIgniter 4')
        );
    }
 
    public function testMakeMenghilangkanSpasiGanda(): void
    {
        $slugger = new Slugger();
 
        $this->assertSame('hello-world', $slugger->make('Hello    World'));
    }
}

Class test extends CIUnitTestCase (subclass PHPUnit\Framework\TestCase). assertSame() mengecek kesamaan nilai dan tipe — pilihan yang lebih ketat daripada assertEquals().

Feature Test

Feature test menjalankan request ke aplikasi dan mengecek respons — menguji controller, route, dan view sekaligus.

tests/feature/PostPageTest.php
<?php
 
namespace Tests\Feature;
 
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;
 
final class PostPageTest extends CIUnitTestCase
{
    use FeatureTestTrait;
 
    public function testHomePageMenampilkanJudul(): void
    {
        $result = $this->call('GET', '/');
 
        $result->assertStatus(200);
        $result->assertSee('Selamat Datang');
    }
 
    public function testPostYangTidakAdaMengembalikan404(): void
    {
        $result = $this->call('GET', '/posts/99999');
 
        $result->assertStatus(404);
    }
 
    public function testDashboardMenolakTanpaLogin(): void
    {
        $result = $this->call('GET', '/dashboard');
 
        $result->assertStatus(302);            // redirect ke login
        $result->assertRedirectTo('/login');
    }
}

FeatureTestTrait memberi method call($method, $path) dan assertion: assertStatus(), assertSee(), assertRedirectTo(). Test ini mensimulasikan HTTP request tanpa menjalankan server — cepat dan deterministik.

Tip

Feature test adalah lapisan pertama proteksi: jika route berubah, response berubah, atau view error — test langsung gagal. Jangan hanya menulis test untuk skenario sukses; uji juga jalur gagal (404, redirect, validation error).

Database Test

Untuk menguji model dengan database nyata, CodeIgniter menyediakan DatabaseTestTrait yang menjalankan migration dan seed otomatis per test.

tests/feature/PostModelTest.php
<?php
 
namespace Tests\Feature;
 
use App\Models\PostModel;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
 
final class PostModelTest extends CIUnitTestCase
{
    use DatabaseTestTrait;
 
    protected $refresh = true;
    protected $namespace = 'App';
 
    public function testModelBisaMenyimpanPost(): void
    {
        $model = new PostModel();
        $id = $model->insert([
            'title'  => 'Test Post',
            'slug'   => 'test-post',
            'body'   => 'Isi test',
            'status' => 'draft',
        ]);
 
        $this->assertIsInt($id);
        $this->seeInDatabase('posts', ['title' => 'Test Post']);
    }
 
    public function testModelMenolakSlugDuplikat(): void
    {
        $model = new PostModel();
        $model->insert([
            'title'  => 'Satu',
            'slug'   => 'satu-satu',
            'body'   => 'x',
            'status' => 'draft',
        ]);
 
        $ok = $model->insert([
            'title'  => 'Dua',
            'slug'   => 'satu-satu',
            'body'   => 'y',
            'status' => 'draft',
        ]);
 
        $this->assertFalse($ok);
        $this->assertNotEmpty($model->errors());
    }
}

$refresh = true membuat migration dan seed berjalan sebelum setiap test, lalu database di-reset setelahnya — setiap test memulai dari kondisi bersih. seeInDatabase() mengecek data benar-benar tersimpan.

Warning

Pastikan database test terpisah dari development. Test model menghapus dan membuat ulang tabel terus-menerus ($refresh = true) — menjalankannya di database ci_blog (development) akan menghapus data kerja kalian.

Mengetes Controller

Controller bisa dites lewat feature test, atau langsung menguji method-nya. Untuk response JSON (episode 13), gunakan assertJSONFragment():

Test API endpoint
public function testApiListMengembalikanJSON(): void
{
    $result = $this->call('GET', '/api/v1/posts');
 
    $result->assertStatus(200);
    $result->assertHeader('Content-Type', 'application/json');
    $result->assertJSONFragment(['status' => 'published']);
}

Menguji controller lewat HTTP layer lebih realistis daripada memanggil method langsung — karena mencakup routing, filter, dan response handling sekaligus.

Ringkasan Testing (PHPUnit)

  • Setup: composer require --dev phpunit/phpunit, jalankan php spark test.
  • Database test terpisah via database.tests.* — jangan sentuh DB development.
  • Unit test (CIUnitTestCase): uji method class secara terisolasi.
  • Feature test (FeatureTestTrait): $this->call(), assertStatus, assertSee, assertRedirectTo.
  • Database test (DatabaseTestTrait): migration + seed per test, seeInDatabase().

Penutup

Inti yang harus dibawa pulang:

  • Test otomatis adalah jaring pengaman untuk refactor dan fitur baru.
  • Feature test menutup routing, controller, filter, dan view sekaligus.
  • Database test memakai database khusus yang di-reset tiap test.
  • Uji jalur sukses dan jalur gagal (404, 302, 422).

Di episode 18 selanjutnya kita akan membahas security hardening (OWASP) — meninjau CSRF, XSS, SQL injection, keamanan file upload, dan security headers, lalu melakukan audit dan patch pada aplikasi kita. Ini episode paling "serius" di series ini!

Belajar CodeIgniter - Testing (PHPUnit) | Belajar CodeIgniter