Tes dalam isolasi
Saat mengembangkan sebuah plugin, cara terbaik untuk mengujinya adalah tanpa memuat lingkungan WordPress.
Jika Anda menulis kode yang dapat dengan mudah diuji tanpa WordPress, kode Anda menjadi lebih baik .
Setiap komponen yang diuji unit, harus diuji secara terpisah : ketika Anda menguji suatu kelas, Anda hanya perlu menguji kelas tersebut, dengan asumsi semua kode lain berfungsi dengan sempurna.
Inilah alasan mengapa unit test disebut "unit".
Sebagai manfaat tambahan, tanpa memuat inti, pengujian Anda akan berjalan jauh lebih cepat.
Hindari kait di konstruktor
Kiat yang bisa saya berikan kepada Anda adalah menghindari memasukkan kait ke dalam konstruktor. Itulah salah satu hal yang akan membuat kode Anda dapat diuji secara terpisah.
Mari kita lihat kode tes di OP:
class CustomPostTypes extends WP_UnitTestCase {
function test_custom_post_type_creation() {
$this->assertTrue( post_type_exists( 'foo' ) );
}
}
Dan mari kita asumsikan tes ini gagal . Siapa pelakunya ?
- kait tidak ditambahkan sama sekali atau tidak benar?
- metode yang mendaftarkan tipe posting tidak dipanggil sama sekali atau dengan argumen yang salah?
- ada bug di WordPress?
Bagaimana itu bisa ditingkatkan?
Anggap kode kelas Anda adalah:
class RegisterCustomPostType {
function init() {
add_action( 'init', array( $this, 'register_post_type' ) );
}
public function register_post_type() {
register_post_type( 'foo' );
}
}
(Catatan: Saya akan merujuk ke versi kelas ini untuk sisa jawabannya)
Cara saya menulis kelas ini memungkinkan Anda untuk membuat instance kelas tanpa memanggil add_action
.
Di kelas di atas ada 2 hal yang harus diuji:
- metode
init
sebenarnya memanggil add_action
lewat untuk argumen yang tepat
- metode
register_post_type
sebenarnya memanggil register_post_type
fungsi
Saya tidak mengatakan bahwa Anda harus memeriksa apakah jenis posting ada: jika Anda menambahkan tindakan yang tepat dan jika Anda menelepon register_post_type
, jenis posting kustom harus ada: jika tidak ada itu adalah masalah WordPress.
Ingat: ketika Anda menguji plugin Anda, Anda harus menguji kode Anda , bukan kode WordPress. Dalam pengujian Anda, Anda harus menganggap bahwa WordPress (seperti perpustakaan eksternal lain yang Anda gunakan) berfungsi dengan baik. Itulah arti dari unit test.
Tapi ... dalam praktek?
Jika WordPress tidak dimuat, jika Anda mencoba memanggil metode kelas di atas, Anda mendapatkan kesalahan fatal, jadi Anda perlu mengejek fungsinya.
Metode "manual"
Tentu Anda dapat menulis perpustakaan mengejek Anda atau "secara manual" mengejek setiap metode. Itu mungkin. Saya akan memberi tahu Anda cara melakukan itu, tetapi kemudian saya akan menunjukkan kepada Anda metode yang lebih mudah.
Jika WordPress tidak dimuat saat tes sedang berjalan, itu berarti Anda dapat mendefinisikan kembali fungsinya, misalnya add_action
atau register_post_type
.
Mari kita asumsikan Anda memiliki file, diambil dari file bootstrap Anda, di mana Anda memiliki:
function add_action() {
global $counter;
if ( ! isset($counter['add_action']) ) {
$counter['add_action'] = array();
}
$counter['add_action'][] = func_get_args();
}
function register_post_type() {
global $counter;
if ( ! isset($counter['register_post_type']) ) {
$counter['register_post_type'] = array();
}
$counter['register_post_type'][] = func_get_args();
}
Saya menulis ulang fungsi untuk hanya menambahkan elemen ke array global setiap kali mereka dipanggil.
Sekarang Anda harus membuat (jika Anda belum memilikinya) memperluas kelas kasus uji dasar Anda PHPUnit_Framework_TestCase
: yang memungkinkan Anda untuk dengan mudah mengkonfigurasi tes Anda.
Itu bisa berupa:
class Custom_TestCase extends \PHPUnit_Framework_TestCase {
public function setUp() {
$GLOBALS['counter'] = array();
}
}
Dengan cara ini, sebelum setiap tes, penghitung global diatur ulang.
Dan sekarang kode pengujian Anda (saya merujuk pada kelas yang ditulis ulang yang saya posting di atas):
class CustomPostTypes extends Custom_TestCase {
function test_init() {
global $counter;
$r = new RegisterCustomPostType;
$r->init();
$this->assertSame(
$counter['add_action'][0],
array( 'init', array( $r, 'register_post_type' ) )
);
}
function test_register_post_type() {
global $counter;
$r = new RegisterCustomPostType;
$r->register_post_type();
$this->assertSame( $counter['register_post_type'][0], array( 'foo' ) );
}
}
Anda harus mencatat:
- Saya dapat memanggil kedua metode secara terpisah dan WordPress tidak dimuat sama sekali. Dengan cara ini jika satu tes gagal, saya tahu persis siapa pelakunya.
- Seperti yang saya katakan, di sini saya menguji bahwa kelas memanggil fungsi WP dengan argumen yang diharapkan. Tidak perlu menguji apakah CPT benar-benar ada. Jika Anda menguji keberadaan CPT, maka Anda menguji perilaku WordPress, bukan perilaku plugin Anda ...
Bagus .. tapi itu PITA!
Ya, jika Anda harus secara manual mengejek semua fungsi WordPress, itu benar-benar menyebalkan. Beberapa saran umum yang dapat saya berikan adalah menggunakan sesedikit mungkin fungsi WP: Anda tidak perlu menulis ulang WordPress, tetapi fungsi WP abstrak yang Anda gunakan di kelas khusus, sehingga dapat diejek dan diuji dengan mudah.
Misalnya mengenai contoh di atas, Anda dapat menulis kelas yang mendaftarkan jenis posting, memanggil register_post_type
'init' dengan argumen yang diberikan. Dengan abstraksi ini Anda masih perlu menguji kelas itu, tetapi di tempat lain dari kode Anda yang mendaftarkan jenis posting Anda dapat menggunakan kelas itu, mengejeknya dalam tes (jadi dengan asumsi itu berfungsi).
Yang luar biasa adalah, jika Anda menulis kelas yang mengabstraksi pendaftaran CPT, Anda dapat membuat repositori terpisah untuknya, dan terima kasih kepada alat-alat modern seperti Composer menanamkannya dalam semua proyek di mana Anda membutuhkannya: uji sekali, gunakan di mana-mana . Dan jika Anda pernah menemukan bug di dalamnya, Anda dapat memperbaikinya di satu tempat dan dengan sederhana composer update
semua proyek di mana ia digunakan juga diperbaiki.
Untuk kedua kalinya: menulis kode yang dapat diuji secara terpisah berarti menulis kode yang lebih baik.
Tapi cepat atau lambat saya harus menggunakan fungsi WP di suatu tempat ...
Tentu saja. Anda tidak boleh bertindak sejajar dengan inti, itu tidak masuk akal. Anda bisa menulis kelas yang membungkus fungsi WP, tetapi kelas-kelas itu perlu diuji juga. Metode "manual" yang diuraikan di atas dapat digunakan untuk tugas-tugas yang sangat sederhana, tetapi ketika sebuah kelas berisi banyak fungsi WP, itu bisa menyebalkan.
Untungnya, di sana ada orang baik yang menulis hal-hal baik. 10up , salah satu agen WP terbesar, memiliki perpustakaan yang sangat bagus untuk orang yang ingin menguji plugin dengan cara yang benar. Itu WP_Mock
.
Hal ini memungkinkan Anda untuk mengejek fungsi WP sebuah kait . Dengan asumsi Anda telah dimuat dalam tes Anda (lihat repo readme) tes yang sama yang saya tulis di atas menjadi:
class CustomPostTypes extends Custom_TestCase {
function test_init() {
$r = new RegisterCustomPostType;
// tests that the action was added with given arguments
\WP_Mock::expectActionAdded( 'init', array( $r, 'register_post_type' ) );
$r->init();
}
function test_register_post_type() {
// tests that the function was called with given arguments and run once
\WP_Mock::wpFunction( 'register_post_type', array(
'times' => 1,
'args' => array( 'foo' ),
) );
$r = new RegisterCustomPostType;
$r->register_post_type();
}
}
Sederhana bukan? Jawaban ini bukan tutorial untuk WP_Mock
, jadi baca repo readme untuk info lebih lanjut, tetapi contoh di atas harus cukup jelas, saya pikir.
Selain itu, Anda tidak perlu menulis apa pun yang dipermainkan add_action
atau sendirian register_post_type
, atau mempertahankan variabel global apa pun.
Dan kelas WP?
WP juga memiliki beberapa kelas, dan jika WordPress tidak dimuat saat Anda menjalankan tes, Anda perlu mengejeknya.
Itu jauh lebih mudah daripada fungsi mengejek, PHPUnit memiliki sistem tertanam untuk mengejek objek, tetapi di sini saya ingin menyarankan Mockery kepada Anda. Ini adalah perpustakaan yang sangat kuat dan sangat mudah digunakan. Selain itu, ini adalah ketergantungan WP_Mock
, jadi jika Anda memilikinya, Anda juga memiliki ejekan.
Tapi bagaimana dengan itu WP_UnitTestCase
?
Suite pengujian WordPress dibuat untuk menguji inti WordPress , dan jika Anda ingin berkontribusi pada inti itu sangat penting, tetapi menggunakannya untuk plugin hanya membuat Anda menguji tidak secara terpisah.
Letakkan mata Anda di dunia WP: ada banyak kerangka kerja PHP dan CMS modern di luar sana dan tidak ada yang menyarankan pengujian plugin / modul / ekstensi (atau apa pun namanya) menggunakan kode kerangka kerja.
Jika Anda melewatkan pabrik, fitur berguna dari suite, Anda harus tahu bahwa ada hal-hal luar biasa di sana.
Gotchas dan kerugiannya
Ada kasus ketika alur kerja yang saya sarankan di sini kurang: pengujian basis data khusus .
Bahkan, jika Anda menggunakan tabel dan fungsi WordPress standar untuk menulis di sana (pada $wpdb
metode level terendah ) Anda tidak perlu benar - benar menulis data atau menguji apakah data benar - benar dalam database, pastikan saja metode yang tepat dipanggil dengan argumen yang tepat.
Namun, Anda dapat menulis plugin dengan tabel kustom dan fungsi yang membuat kueri untuk ditulis di sana, dan menguji apakah kueri itu berfungsi, itu adalah tanggung jawab Anda.
Dalam kasus-kasus itu, WordPress test suite dapat banyak membantu Anda, dan memuat WordPress mungkin diperlukan dalam beberapa kasus untuk menjalankan fungsi-fungsi seperti dbDelta
.
(Tidak perlu dikatakan untuk menggunakan db yang berbeda untuk tes, bukan?)
Untungnya PHPUnit memungkinkan Anda untuk mengatur tes Anda di "suite" yang dapat dijalankan secara terpisah, sehingga Anda dapat menulis suite untuk tes basis data khusus tempat Anda memuat lingkungan WordPress (atau sebagian darinya) meninggalkan semua tes Anda bebas WordPress .
Pastikan untuk menulis kelas yang abstrak sebanyak mungkin operasi basis data, dengan cara semua kelas plugin lainnya memanfaatkannya, sehingga dengan mengolok-olok Anda dapat dengan benar menguji mayoritas kelas tanpa berurusan dengan basis data.
Untuk ketiga kalinya, menulis kode mudah diuji dalam isolasi berarti menulis kode yang lebih baik.
phpunit
, dapatkah Anda melihat tes yang gagal atau lulus? Apakah Anda menginstalbin/install-wp-tests.sh
?