Apa Manfaat Facade Di Laravel?


Salah satu pertanyaan yang paling sering ditanyakan oleh mereka yang baru mencoba Laravel adalah apa gunanya facade? Seperti yang diketahui, facade adalah sebuah fitur dari Laravel yang mengimplementasikan facade pattern (http://en.wikipedia.org/wiki/Facade_pattern). Pada facade pattern, terdapat sebuah class yang disebut facade class yang akan mengakses class lain. Class pengguna (client) tidak akan mengakses class yang di-‘facade’ secara langsung melainkan mengaksesnya melalui facade class. Tujuan dari pattern ini adalah untuk menyembunyikan kerumitan.

Pada Laravel, facade diimplementasikan dalam bentuk sebuah class dengan method static yang bila dipanggil akan memanggil method di object lain yang di-‘facade’ olehnya. Sebagai contoh facade Log::debug() akan memanggil method debug() dari sebuah objek Monolog\Logger di IoC Container. Dalam hal ini, facade dapat dianggap sebagai cara singkat dalam mengakses sebuah object di container.

Tapi facade memiliki peran penting lainnya yang berkaitan dengan unit testing (atau tepatnya integration testing atau functional testing). Untuk menunjukkannya, saya akan membuat sebuah functional test yang isinya seperti berikut ini:

<?php

class PelangganControllerTest extends TestCase {

    public function testTambahSukses() {
        // Mengirim semua data yang dibutuhkan untuk disimpan
        $response = $this->call('GET', 'pelanggan/tambah',
            ['email'=>'solid@snake.com', 'nama'=>'Solid Snake']);

        $this->assertJson($response->getContent());
        $this->assertEquals('solid@snake.com', $response->getData()->email);
    }

    public function testTambahGagal() {
        // Hanya mengirim 'nama' tanpa 'email'
        $response = $this->call('GET', 'pelanggan/tambah',
            ['nama'=>'Solid Snake']);        

        $this->assertJson($response->getContent());
        $this->assertEquals('Email tidak boleh kosong', $response->getData()->kesalahan);
    }

}

?>

Seperti biasa, bila saya menjalankan unit test di atas, saya akan memperoleh pesan kesalahan karena saya belum membuat controller yang bersangkutan. Untuk itu, saya segera membuat controller dengan nama PelangganController.php di direktori app/controllers yang isinya seperti berikut ini:

<?php

class PelangganController extends BaseController {

    public function getTambah()
    {       
        $email = Input::get('email');
        $nama = Input::get('nama');
        if (empty($email)) {
            $hasil['kesalahan'] = 'Email tidak boleh kosong';
        } else {
            mail($email, 'Registrasi', "Selamat, $email.  Anda sudah terdaftar!", "From:me");
            $hasil['email'] = $email;
        }
        return Response::json($hasil);
    }

}

?>

Pada kode program di atas, bila pengguna mengisi alamat email, maka kode tersebut akan mengirim pesan ke alamat email yang bersangkutan melalui function mail() dari PHP. Agar controller di atas dapat dipakai, saya akan mendaftarkannya di routes.php dengan menambahkan kode program berikut ini:

Route::controller('pelanggan', 'PelangganController');

Setelah ini, bila saya menjalankan unit test, saya akan menemukan sebuah kegagalan seperti yang terlihat pada gambar berikut ini:

Test yang gagal

Test yang gagal

Hal ini terjadi karena saya memanggil mail() dari PHP dan saya tidak men-setup mail server di komputer lokal. Walaupun seandainya saya sudah men-setup mail server, hal ini tetap jadi masalah karena setiap kali saya menjalankan unit test, ia akan berusaha mengirim email yang sesungguhnya.

Untuk mengatasi masalah seperti ini, saya dapat mengubah kode program di atas agar tidak memanggil mail() secara langsung, melainkan memanggil facade yang telah disediakan oleh Laravel, yaitu Mail. Oleh sebab itu, saya mengubah kode program PelangganController.php menjadi seperti berikut ini:

<?php

class PelangganController extends BaseController {

    public function getTambah()
    {       
        $email = Input::get('email');
        $nama = Input::get('nama');
        if (empty($email)) {
            $hasil['kesalahan'] = 'Email tidak boleh kosong';
        } else {
            Mail::send('emails.registrasi', ['email'=>$email], function($message) use ($email) {
                $message->from('me@snake.com', 'Solid Snake');
                $message->to($email)->subject('Registrasi!');
            });         
            $hasil['email'] = $email;
        }
        return Response::json($hasil);
    }

}

?>

Class Mail adalah facade ke sebuah objek Illuminate\Mail\Mailer. Perhatikan bahwa saya memakai class secara static tapi sebenarnya ada sebuah object yang saya akses yaitu instance dari Illuminate\Mail\Mailer. Object tersebut dibuat oleh Illuminate\Mail\MailServiceProvider dengan perintah seperti new Mailer().

Bila saya menjalankan pengujian, saya tetap akan memperoleh pesan kesalahan. Lalu, apa bedanya? Seluruh facade di Laravel dilengkapi dengan kemampuan mocking dengan menggunakan Mockery, misalnya melalui method shouldReceive() dan sebagainya. Tapi karena saya tidak memakai Mockery, saya bisa memakai fasilitas mocking dari PHPUnit. Sebagai contoh, saya mengubah kode program pengujian menjadi seperti berikut ini:

<?php

class PelangganControllerTest extends TestCase {

    public function testTambahSukses() {
        // Mocking dari Mailer
        $mailer = $this->getMock('Mailer', array('send'));
        $mailer->expects($this->once())->method('send');

        // Mendaftarkan hasil mock ke facade
        $mailer = Mail::swap($mailer);

        // Mengirim semua data yang dibutuhkan untuk disimpan
        $response = $this->call('GET', 'pelanggan/tambah',
            ['email'=>'solid@snake.com', 'nama'=>'Solid Snake']);

        // Mengembalikan facade seperti semula
        $mailer = Mail::swap($mailer);          

        // Pengujian
        $this->assertJson($response->getContent());
        $this->assertEquals('solid@snake.com', $response->getData()->email);     
    }

    public function testTambahGagal() {
        // Hanya mengirim 'nama' tanpa 'email'
        $response = $this->call('GET', 'pelanggan/tambah',
            ['nama'=>'Solid Snake']);        

        $this->assertJson($response->getContent());
        $this->assertEquals('Email tidak boleh kosong', $response->getData()->kesalahan);
    }

}

?>

Mock adalah sebuah class ‘palsu’ yang menyerupai aslinya. Dengan class ‘palsu’ ini, saya bisa melakukan assertions seperti memastikan ada method tertentu yang dipanggil tanpa harus benar-benar memanggil method bersangkutan. Sebagai contoh, saya membuat mock dari Illuminate\Mail\Mailer dan memastikan bahwa method send() akan dipanggil sekali. Karena ini hanya class ‘palsu’, tidak ada proses pengiriman email yang akan terjadi, hanya saja saya menyakinkan diri bahwa method send() akan dipanggil.

Sekarang, bila saya menjalankan pengujian, saya akan memperoleh hasil sukses seperti pada gambar berikut ini:

Hasil pengujian yang sukses

Hasil pengujian yang sukses

Terlihat bahwa facade mempermudah pengujian bila dibandingkan dengan saya memanggil fungsi yang diharapkan langsung pada kode program.

Sebagai informasi tambahan, khusus untuk Mail, sebenarnya Laravel sudah memiliki cara yang lebih singkat dan lebih mudah untuk tidak benar-benar mengirim email. Saya bisa mengubah file konfigurasi di app/config/testing/mail.php (atau men-copy-nya bila belum ada) dimana nilai 'pretend' di-set menjadi true, seperti pada contoh berikut ini:

<?php

return array(

    ...

    'pretend' => true,

);

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: