Pengembangan berbasis pengujian dan pengujian unit adalah cara terbaru untuk memastikan bahwa kode terus berfungsi seperti yang diharapkan meskipun ada modifikasi dan perubahan besar. Pada artikel ini, Anda akan mempelajari cara menguji unit kode PHP Anda pada lapisan modul, database, dan antarmuka pengguna (UI).
Ini jam 3 pagi. Bagaimana kita tahu bahwa kode kita masih berfungsi?
Aplikasi web berjalan 24x7, jadi pertanyaan apakah program saya masih berjalan akan mengganggu saya di malam hari. Pengujian unit telah membantu saya membangun kepercayaan diri yang cukup pada kode saya sehingga saya dapat tidur nyenyak.
Pengujian unit adalah kerangka kerja untuk menulis kasus pengujian untuk kode Anda dan menjalankan pengujian ini secara otomatis. Pengembangan berbasis pengujian adalah pendekatan pengujian unit yang didasarkan pada gagasan bahwa Anda harus menulis pengujian terlebih dahulu dan memverifikasi bahwa pengujian tersebut dapat menemukan kesalahan, baru kemudian mulai menulis kode yang harus lulus pengujian tersebut. Jika semua pengujian lulus, fitur yang kami kembangkan telah selesai. Nilai dari pengujian unit ini adalah kita dapat menjalankannya kapan saja—sebelum memeriksa kode, setelah perubahan besar, atau setelah penerapan ke sistem yang sedang berjalan.
Pengujian Unit PHP
Untuk PHP, kerangka pengujian unit adalah PHPUnit2. Sistem ini dapat diinstal sebagai modul PEAR menggunakan baris perintah PEAR: % pear install PHPUnit2.
Setelah menginstal kerangka kerja, Anda dapat menulis pengujian unit dengan membuat kelas pengujian yang berasal dari PHPUnit2_Framework_TestCase.
Pengujian Unit Modul
Saya telah menemukan bahwa tempat terbaik untuk memulai pengujian unit adalah dalam modul logika bisnis aplikasi. Saya menggunakan contoh sederhana: ini adalah fungsi yang menjumlahkan dua angka. Untuk memulai pengujian, pertama-tama kita tulis test case seperti gambar di bawah ini.
Daftar 1. TestAdd.php
<?phprequire_once 'Add.php';require_once 'PHPUnit2/Framework/TestCase.php';kelas TestAdd extends PHPUnit2_Framework_TestCase{ function test1() { $this->assertTrue( add( 1, 2 ) == 3 ); } function test2() { $this->assertTrue( add( 1, 1 ) == 2 }}?>
Kelas TestAdd ini memiliki dua metode, keduanya menggunakan awalan tes. Setiap metode mendefinisikan pengujian, yang bisa sesederhana Listing 1 atau sangat kompleks. Dalam hal ini, kita cukup menyatakan bahwa 1 ditambah 2 sama dengan 3 pada pengujian pertama, dan 1 ditambah 1 sama dengan 2 pada pengujian kedua.
Sistem PHPUnit2 mendefinisikan metode asserTrue(), yang digunakan untuk menguji apakah nilai kondisi yang terdapat dalam parameter benar. Kami kemudian menulis modul Add.php, yang awalnya memberikan hasil yang salah.
Listing 2. Add.php
<?phpfunction add( $a, $b ) { return 0; }?>
Sekarang saat menjalankan pengujian unit, kedua pengujian gagal.
Listing 3. Kegagalan pengujian
% phpunit TestAdd.phpPHPUnit 2.2.1 oleh Sebastian Bergmann.FFTime: 0.0031270980834961Ada 2 kegagalan:1) test1(TestAdd)2) test2(TestAdd)FAILURES!!!Tes dijalankan: 2, Kegagalan: 2, Kesalahan: 0, Tes Tidak Lengkap: 0.
Sekarang saya tahu kedua tes berfungsi dengan baik. Oleh karena itu, fungsi add() dapat dimodifikasi agar benar-benar melakukan hal yang sebenarnya.
Kedua tes sekarang lulus.
<?phpfunction add( $a, $b ) { return $a+$b;>
Listing 4. Tes lulus
% phpunit TestAdd.phpPHPUnit 2.2.1 oleh Sebastian Bergmann...Waktu: 0,0023679733276367OK (2 tes)%
meskipun Contoh pengembangan berbasis pengujian ini sangat sederhana, namun kita dapat memahami gagasannya. Kami pertama kali membuat kasus pengujian dan memiliki kode yang cukup untuk menjalankan pengujian, namun hasilnya salah. Kemudian kami memverifikasi bahwa pengujian tersebut memang gagal, dan kemudian menerapkan kode sebenarnya agar pengujian tersebut lulus.
Saya menemukan bahwa ketika saya menerapkan kode, saya terus menambahkan kode sampai saya memiliki tes lengkap yang mencakup semua jalur kode. Di akhir artikel ini, Anda akan menemukan beberapa saran tentang tes apa yang harus ditulis dan bagaimana cara menulisnya.
Pengujian basis data
Setelah pengujian modul, pengujian akses basis data dapat dilakukan. Pengujian akses database memunculkan dua isu menarik. Pertama, kita harus memulihkan database ke titik tertentu sebelum setiap pengujian. Kedua, perlu diketahui bahwa pemulihan ini dapat menyebabkan kerusakan pada database yang ada, jadi kita harus menguji pada database non-produksi, atau berhati-hati agar tidak mempengaruhi konten database yang ada saat menulis kasus uji.
Pengujian unit database dimulai dari database. Untuk mengilustrasikan masalah ini, kita perlu menggunakan pola sederhana berikut.
Listing 5. Schema.sql
DROP TABLE IF EXISTS penulis;CREATE TABLE author (id MEDIUMINT NOT NULL AUTO_INCREMENT, name TEXT NOT NULL, PRIMARY KEY (id));
Listing 5 adalah tabel penulis, dan setiap record memiliki ID terkait.
Selanjutnya, Anda dapat menulis kasus uji.
Daftar 6. TestAuthors.php
<?phprequire_once 'dblib.php';require_once 'PHPUnit2/Framework/TestCase.php';kelas TestAuthors memperluas PHPUnit2_Framework_TestCase{ function test_delete_all() { $this->assertTrue( Authors::delete_all() ); } fungsi test_insert() { $this->assertTrue( Penulis::delete_all() ); $ini->assertTrue( Penulis::insert( 'Jack' ) } fungsi test_insert_and_get() { $this->assertTrue( Penulis ::delete_all() ); $ini->assertTrue( Penulis::insert( 'Jack' ) ); $ini->assertTrue( Penulis::insert( 'Joe' ) ); ; $this->assertTrue( $found != null ); $this->assertTrue( count( $found ) == 2 }}?>
Rangkaian pengujian ini mencakup penghapusan penulis dari tabel dan memasukkan penulis ke dalam tabel Serta fungsi seperti menyisipkan penulis sambil memverifikasi keberadaan penulis. Ini adalah tes kumulatif, yang menurut saya sangat berguna untuk menemukan bug. Dengan mengamati tes mana yang berhasil dan mana yang tidak, Anda dapat dengan cepat mengetahui apa yang salah dan kemudian memahami lebih jauh perbedaannya.
Versi kode akses database PHP dblib.php yang awalnya menyebabkan kegagalan ditunjukkan di bawah ini.
Daftar 7. dblib.php
<?phprequire_once('DB.php'); kelas Penulis{ fungsi statis publik get_db() { $dsn = 'mysql://root:password@localhost/unitdb'; :Hubungkan( $dsn, array() ); if (PEAR::isError($db)) { die($db->getMessage() } kembalikan $db } fungsi statis publik delete_all() { return false; } public static function insert( $name ) { return false; } public static function get_all() { return null; }}?>
Menjalankan pengujian unit pada kode di Listing 8 akan menunjukkan bahwa ketiga pengujian gagal:
Listing 8. dblib. php
% phpunit TestAuthors.phpPHPUnit 2.2.1 oleh Sebastian Bergmann.FFFTime: 0.007500171661377Ada 3 kegagalan:1) test_delete_all(TestAuthors)2) test_insert(TestAuthors)3) test_insert_and_get(TestAuthors)FAILURES!!!Tes yang dijalankan: 3, Kegagalan: 3, Kesalahan: 0, Tes Tidak Lengkap: 0.%
Sekarang kita dapat mulai menambahkan kode untuk mengakses database dengan benar - metode demi metode - hingga ketiga tes lulus. Versi final kode dblib.php ditunjukkan di bawah ini.
Listing 9. Lengkapi dblib.php
<?phprequire_once('DB.php');class Authors{ public static function get_db() { $dsn = 'mysql://root:password@localhost/unitdb'; ::Connect( $dsn, array() ); if (PEAR::isError($db)) { die($db->getMessage()); kembalikan $db; = Penulis::get_db(); $sth = $db->siapkan( 'HAPUS DARI penulis' ); $db->eksekusi( $sth } sisipkan fungsi statis publik( $nama ) { $db = Penulis::get_db(); $sth = $db->prepare( 'MASUKKAN KE NILAI penulis (null,?)' ); $db->execute( $sth, array( $name ) ); fungsi statis get_all() { $db = Penulis::get_db(); $res = $db->query( "PILIH * DARI penulis" ); $baris = array(); ) ) { $rows []= $row; } return $rows; }}?>
Pengujian HTML
Langkah selanjutnya dalam pengujian seluruh aplikasi PHP adalah menguji antarmuka Hypertext Markup Language (HTML). Untuk melakukan pengujian ini, kita memerlukan halaman Web seperti di bawah ini.
Daftar 10. TestPage.php
<?phprequire_once 'HTTP/Client.php';require_once 'PHPUnit2/Framework/TestCase.php';class TestPage extends PHPUnit2_Framework_TestCase{ function get_page( $url ) { $client = new HTTP_Client(); ->dapatkan( $url ); $resp = $klien->currentResponse(); kembalikan $resp['body']; fungsi test_get() { $halaman = TestPage::get_page( 'http://localhost/unit /add.php' ); $ini->assertTrue( strlen( $halaman ) > 0 ); $ini->assertTrue( preg_match( '/<html>/', $halaman ) == 1 ); ) { $halaman = TestPage::get_page( 'http://localhost/unit/add.php?a=10&b=20' ); $ini->assertTrue( strlen( $halaman ) > 0 $ini-> menegaskanTrue( preg_match( '/<html>/', $halaman ) == 1 ); preg_match( '/<span id="hasil">(.*?)</span>/', $halaman, $keluar ); $this->assertTrue( $out[1]=='30' }}?>
Pengujian ini menggunakan modul Klien HTTP yang disediakan oleh PEAR. Menurut saya ini sedikit lebih sederhana daripada Perpustakaan URL Klien PHP (CURL) bawaan, tetapi yang terakhir juga dapat digunakan.
Ada tes yang memeriksa halaman yang dikembalikan dan menentukan apakah halaman tersebut berisi HTML. Pengujian kedua meminta jumlah 10 dan 20 dengan menempatkan nilai di URL yang diminta, lalu memeriksa hasilnya di halaman yang dikembalikan.
Kode untuk halaman ini ditunjukkan di bawah.
Daftar 11. TestPage.php
<html><body><form><input type="text" name="a" value="<?php echo($_REQUEST['a']); ?>" /> + <input type="text" name="b" value="<?php echo($_REQUEST['b']); ?>" /> =<span id="result"><?php echo($_REQUEST ['a']+$_REQUEST['b']); ?></span><br/><input type="submit" value="Tambahkan" /></form></body></html >
Halaman ini cukup sederhana. Kedua kolom input menampilkan nilai saat ini yang diberikan dalam permintaan. Rentang hasil menunjukkan jumlah dari dua nilai ini. Markup menandai semua perbedaan: tidak terlihat oleh pengguna, tetapi terlihat oleh pengujian unit. Jadi pengujian unit tidak memerlukan logika rumit untuk menemukan nilai ini. Sebaliknya, ini mengambil nilai tag tertentu. Dengan cara ini, ketika antarmuka berubah, selama rentangnya ada, pengujian akan berhasil.
Seperti sebelumnya, tulis kasus uji terlebih dahulu lalu buat versi halaman yang gagal. Kami menguji kegagalan dan kemudian memodifikasi konten halaman agar berfungsi. Hasilnya sebagai berikut:
Listing 12. Tes gagal, lalu modifikasi halaman
% phpunit TestPage.phpPHPUnit 2.2.1 oleh Sebastian Bergmann...Waktu: 0.25711488723755OK (2 tes)%
Kedua tes bisa lulus, artinya tes tersebut kode Ini berfungsi dengan baik.
Saat menjalankan pengujian pada kode ini, semua pengujian berjalan tanpa masalah, jadi kami tahu bahwa kode kami berfungsi dengan benar.
Namun pengujian front-end HTML memiliki kelemahan: JavaScript. Kode klien Hypertext Transfer Protocol (HTTP) mengambil halaman, tetapi tidak mengeksekusi JavaScript. Jadi jika kita memiliki banyak kode dalam JavaScript, kita harus membuat pengujian unit tingkat agen pengguna. Cara terbaik yang saya temukan untuk mencapai fungsi ini adalah dengan menggunakan fungsionalitas lapisan otomatisasi yang ada di Microsoft® Internet Explorer®. Dengan skrip Microsoft Windows® yang ditulis dalam PHP, Anda dapat menggunakan antarmuka Component Object Model (COM) untuk mengontrol Internet Explorer untuk menavigasi antar halaman, dan kemudian menggunakan metode Document Object Model (DOM) untuk menemukan halaman setelah melakukan tindakan pengguna tertentu .
Ini adalah satu-satunya cara yang saya tahu untuk menguji unit kode JavaScript front-end. Saya akui tidak mudah untuk menulis dan memeliharanya, dan pengujian ini mudah rusak bahkan jika ada sedikit perubahan yang dilakukan pada halamannya.
Tes mana yang harus ditulis dan bagaimana cara menulisnya
Saat menulis tes, saya ingin membahas skenario berikut:
Semua tes positif
Serangkaian tes ini memastikan bahwa semuanya berjalan sesuai harapan.
Semua pengujian negatif
menggunakan pengujian ini satu per satu untuk memastikan bahwa setiap kegagalan atau anomali diuji.
Pengujian Urutan Positif
Serangkaian pengujian ini memastikan bahwa panggilan dalam urutan yang benar berfungsi seperti yang kita harapkan.
Pengujian Urutan Negatif
Rangkaian pengujian ini memastikan bahwa panggilan gagal bila panggilan tersebut tidak dilakukan dalam urutan yang benar.
Pengujian Beban
Apabila diperlukan, serangkaian pengujian kecil dapat dilakukan untuk menentukan bahwa kinerja pengujian ini sesuai dengan harapan kami. Misalnya, 2.000 panggilan akan selesai dalam waktu 2 detik.
Pengujian Sumber Daya
Pengujian ini memastikan bahwa antarmuka pemrograman aplikasi (API) mengalokasikan dan melepaskan sumber daya dengan benar - misalnya, memanggil API berbasis file buka, tulis, dan tutup beberapa kali berturut-turut untuk memastikan tidak ada file yang masih terbuka.
Pengujian panggilan balik
Untuk API yang memiliki metode panggilan balik, pengujian ini memastikan bahwa kode berjalan normal jika tidak ada fungsi panggilan balik yang ditentukan. Selain itu, pengujian ini juga dapat memastikan bahwa kode masih dapat berjalan normal ketika fungsi panggilan balik ditentukan tetapi fungsi panggilan balik tersebut beroperasi dengan tidak benar atau menghasilkan pengecualian.
Berikut adalah beberapa pemikiran tentang pengujian unit. Saya punya beberapa saran tentang cara menulis pengujian unit:
Jangan gunakan data acak
.Meskipun membuat data acak di antarmuka mungkin tampak seperti ide yang bagus, kami ingin menghindari melakukannya karena data ini bisa menjadi sangat sulit untuk di-debug. Jika data dihasilkan secara acak pada setiap panggilan, mungkin terjadi kesalahan pada satu pengujian tetapi tidak pada pengujian lainnya. Jika pengujian Anda memerlukan data acak, Anda dapat membuatnya dalam sebuah file dan menggunakan file tersebut setiap kali Anda menjalankannya. Dengan pendekatan ini, kami mendapatkan beberapa data yang "berisik", namun kami masih dapat men-debug kesalahan.
Pengujian kelompok
Kami dapat dengan mudah mengumpulkan ribuan pengujian yang memerlukan waktu beberapa jam untuk dijalankan. Tidak ada yang salah dengan hal itu, namun mengelompokkan pengujian ini memungkinkan kami menjalankan serangkaian pengujian dengan cepat dan memeriksa masalah utama, lalu menjalankan serangkaian pengujian lengkap di malam hari.
Menulis API dan Pengujian yang Kuat
Penting untuk menulis API dan pengujian agar tidak mudah rusak ketika fungsionalitas baru ditambahkan atau fungsionalitas yang sudah ada diubah. Tidak ada solusi ajaib yang universal, namun aturan praktisnya adalah pengujian yang "berombang-ambing" (terkadang gagal, terkadang berhasil, berulang kali) harus segera dibuang.
Kesimpulan
Pengujian unit sangat penting bagi para insinyur. Mereka adalah fondasi untuk proses pengembangan yang tangkas (yang sangat menekankan pada pengkodean karena dokumentasi memerlukan beberapa bukti bahwa kode tersebut berfungsi sesuai dengan spesifikasi). Tes unit memberikan bukti ini. Prosesnya dimulai dengan pengujian unit, yang menentukan fungsionalitas yang harus diimplementasikan oleh kode, tetapi saat ini tidak. Oleh karena itu, semua pengujian pada awalnya akan gagal. Kemudian ketika kodenya hampir selesai, pengujiannya lulus. Ketika semua tes lulus, kodenya menjadi sangat lengkap.
Saya tidak pernah menulis kode besar atau memodifikasi blok kode yang besar atau kompleks tanpa menggunakan pengujian unit. Saya biasanya menulis pengujian unit untuk kode yang ada sebelum saya memodifikasinya, hanya untuk memastikan saya tahu apa yang saya langgar (atau tidak langgar) ketika saya memodifikasi kode. Ini memberi saya keyakinan besar bahwa kode yang saya berikan kepada klien saya berjalan dengan benar - bahkan pada jam 3 pagi.