Nafies Luthfi

Life will always feel wonderful if we always think positively.

Testing Laravel: Testing Validasi Form

Bismillahirrahmanirrahim

Kita lanjutkan membuat validasi form input task. Mungkin teman-teman sudah melihat, ketika membuka aplikasi pada url /tasks, kemudian langsung tekan tombol Create Task (submit form) tanpa mengisi field name dan description, kita dapatkan error seperti ini.

Error SQL tanpa validasi

Error ini disebabkan karena kolom name tidak boleh bernilai null saat diinput ke database. Sebagaimana file migration untuk table tasks : xxxxx_create_tasks_table.php, name dan description tidak diset nullable. Error ini juga akan terjadi jika kolom description kosong.

Solusinya, kita harus buat validasi form input task ini, untuk memastikan user menginput data dengan benar. Karena misi kita menerapkan TDD, maka kita juga buat testing sebelum membuat validasi form.

Di sini starting point saya berasal dari commit repo ini pada artikel sebelumnya. Teman-teman silakan teruskan project sendiri jika sudah mengikuti artikel ini dari awal.

Membuat Testing untuk menguji validasi form

Seperti biasa sebelum mulai kerja, kita pastikan semua testing passed.

# 1
$ vendor/bin/phpunit

OK (6 tests, 13 assertions)

Sip, kita lanjutkan. Buka file tests/Feature/ManageTasksTest.php, tambahkan method test baru seperti berikut :

<?php
    // Kelas ManageTasksTest.php
    
    // ... user_can_create_a_task()

    /** @test */
    public function task_entry_must_pass_validation()
    {
        // Submit form untuk membuat task baru
        // dengan field name description kosong
        $this->post('/tasks', [
            'name'        => '',
            'description' => '',
        ]);

        // Cek pada session apakah ada error untuk field nama dan description
        $this->assertSessionHasErrors(['name', 'description']);
    }
Hasil: Error
Session missing key: errors
Failed asserting that false is true.
Penyebab

Belum muncul error (dari validasi) pada session, karena kita belum membuat validasi pada TasksController@store.

Solusi

Tambahkan script validasi Laravel untuk field name dan description.

<?php
// TasksController

    // ... method index
    
    public function store()
    {
        request()->validate([
            'name'        => 'required|max:255',
            'description' => 'required|max:255',
        ]);

        Task::create(request()->only('name', 'description'));

        return back();
    }
# 2
$ vendor/bin/phpunit

OK (7 tests, 16 assertions)

Sip. Kita sudah dapat hijau lagi. Sekarang kita coba buka aplikasi dari browser.

Menampilkan error validasi form

Kita coba klik tombol Create Task (submit form). Nah, error SQL sebelumnya sudah hilang. Kita sudah redirect ke halaman sebelumnya, tetapi belum ada tampilan pesan (error) validasi form. Oke, kita tambahkan script untuk menampilkan error validasinya pada view tasks.index.

    <!-- ... -->
    <div class="col-md-4">
        <h2>New Task</h2>

        @if (count($errors) > 0)
            <div class="alert alert-danger">
                <ul class="list-unstyled">
                    @foreach ($errors->all() as $error)
                        <li>{% raw" >}}{{ $error }}{% endraw" >}}</li>
                    @endforeach
                </ul>
            </div>
        @endif

        <form action="{% raw" >}}{{ url('tasks') }}{% endraw" >}}" method="post">
            <!-- ... -->
        </form>
    </div>
    <!-- ... -->

Simpan, kita kembali ke browser dan coba klik Create Task lagi. Seharusnya kita dapatkan tampilan seperti ini, pesan dari validasi form sudah muncul.

Tampil error validasi

Sekarang kita coba jalankan PHPUnit, untuk memastikan tidak ada yang error/kerusakan dari perubahan barusan.

# 3
$ vendor/bin/phpunit

Testing tetap passed setelah ditambahkan validasi form

Ok, sampai di sini selesai penambahan fitur validasi untuk form Input Task.

Mungkin sebagian teman-teman ada yang bertanya, kenapa kita menggunakan $this->post() saat menguji validasi form, bukan menggunakan $this->submitForm() seperti saat testing input task.

Perbedaan “$this->submitForm()” dengan “$this->post()”

  1. Pada $this->submitForm()
    Paket browserkit testing berinteraksi dengan halaman web, dia mendeteksi halaman web dengan bantuan DOM Crawler Symfony. Yaitu mencari komponen html berupa tag form yang didalamnya ada tombol submit dengan tulisan Create Task, mencari input field bernama name dan description, kemudian melakukan submit form seolah-olah menekan tombol submit Create Task tadi. Method $this->submitForm() ini juga dapat menerima respons saat aplikasi redirect ke halaman lain.

    Kekurangan dari method $this->submitForm() ini, dia tidak mendeteksi isi session Laravel. Sehingga pesan validasi form yang ingin kita cek (di testing) pada session Laravel tidak terlihat. (Mohon koreksi jika saya keliru paham tentang ini).

  2. Pada $this->post()
    Paket browserkit testing berinteraksi dengan HTTP Request langsung ke URL yang dituju (misal /tasks) dengan method post.

    Method $this->post() ini tidak perduli berinteraksi dengan halaman web, dia langsung mengirim request ke URL tujuan (dengan method yang sesuai). Method ini dapat berinteraksi dengan session Laravel, karenanya kita dapat melihat session yang berisi pesan (error) validasi form.

    Kekurangan dari method $this->post() ini, dia mengabaikan form yang kita buat di view. Ketika kita melakukan perubahan field di view, misal nama field description diubah menjadi deskripsi_task (tetapi pada testing tidak disesuaikan), method ini tidak perduli dengan perubahan itu (testing tetap passed), karena dia langsung melakukan request ke URL yang dituju.

    Sedangkan method $this->submitForm(), dia akan berpengaruh terhadap perubahan ini. Testing akan fail karena nama field di view berbeda dengan yang di script testing.

    BTW, selain $this->post(), ada juga $this->get(), $this->patch(), $this->put(), dan $this->delete(). Masing-masing method ini akan berinteraksi dengan method yang sesuai saat kita mendifinisikan Route pada file routes/web.php, misal Route::get(), Route::post(), Route::patch(), Route::put(), dan Route::delete().

Jadi antara kedua metode submit form tersebut, saya pribadi lebih sering menggunakan $this->submitForm() dalam menguji sebuah fitur CRUD, karena dia beriteraksi dengan halaman web dan elemen-elemen form. Supaya ketika kita mengutak-atik form di view, tetapi lupa menyesuaikan testingnya, testing akan fail (segera ketahuan). Jadi hasilnya lebih pasti dan meyakinkan. :)

Baik teman-teman, semoga artikel ini mudah dimengerti. Jika ada yang belum dipahami atau kurang jelas. Silakan disampaikan di komentar. Perubahan script di atas dapat dilihat pada commit repo ini.

Terima kasih sudah berkenan membaca.