Repositori untuk lokakarya saya di WordCamp Catania 2019
Opsional, tetapi Anda mungkin perlu menginstal buruh pelabuhan:
sudo apt-get install docker-ce docker-ce-cli containerd.io
Saya berasumsi Anda telah menginstal Komposer. Mari kita instal PHPUnit terlebih dahulu:
composer require --dev phpunit/phpunit ^8.3
Silakan periksa juga persyaratannya!
PHPUnit 8.3 membutuhkan setidaknya PHP 7.2! Omong-omong - dukungan keamanan untuk PHP 7.1 berakhir pada 1 Desember 2019.
Petunjuk : Anda belum menginstal Komposer ? Coba ini!
docker run --rm -it -v $PWD:/app -u $(id -u):$(id -g) composer install
Setidaknya ada dua kerangka kerja valid yang berguna saat Anda berencana menguji ekstensi WordPress:
Mari kita coba Otak Monyet :
composer require --dev brain/monkey:2.*`
Ini secara otomatis juga akan menginstal Mockery dan Patchwork. Cukup jalankan composer install
dan Anda siap melakukannya.
Buat direktori yang akan menjadi rumah bagi kelas pengujian kecil bernama WcctaTest.php :
mkdir -p tests/wccta
Bagus sekali! Sekarang mari kita buat file konfigurasi phpunit.xml di direktori root.
Anda juga dapat memutuskan untuk menjalankan pengujian dengan parameter konfigurasi dari baris perintah. Lihat bagian selanjutnya (petunjuk: 'skrip')!
Besar! Tambahkan beberapa bagian ke file composer.json :
composer test
Mari kita buat direktori yang akan menjadi rumah bagi kode sumber kita. Ini adalah tempat di mana Anda akan menempatkan kelas pertama yang akan segera Anda uji.
mkdir -p src/wccta && touch src/wccta/Plugin.php
rm -f tests/wccta/WcctaTest.php && touch tests/wccta/PluginTest.php
touch wordpress-plugins-phpunit.php
Kami ingin menguji beberapa metode kelas Plugin
. Bayangkan sebuah metode bernama is_loaded
yang mengembalikan true
jika berhasil. Saat Anda siap, jalankan:
composer test
Petunjuk : Sistem atau versi PHP Anda tidak mutakhir? Anda dapat melewati langkah ini, tetapi mari kita coba sesuatu yang [tidak terlalu] baru!
docker run -it --rm -v $PWD:/app -w /app php:7.3-alpine php ./vendor/bin/phpunit
Anda mungkin dapat membayangkan bahwa beberapa plugin memiliki banyak kelas dan Anda dapat dengan mudah lupa menguji semua fungsi yang memerlukan pengujian.
Jadi, mari kita bicara tentang Cakupan !
Cukup tambahkan perintah khusus ke bagian scripts di composer.json Anda:
"coverage": "./vendor/bin/phpunit --coverage-html ./reports/php/coverage"
dan filter ke phpunit.xml Anda:
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory>./src</directory>
</whitelist>
</filter>
Sekarang jalankan composer coverage
! Ini akan membuat direktori ./reports/php/coverage
bersama dengan beberapa file html. Ya, tidak di semua komputer. Beberapa masih akan mendapatkan pesan kesalahan seperti:
Error: No code coverage driver is available
Mari kita perbaiki di docker-image kita. Saya menyiapkan Dockerfile sehingga Anda bisa menjalankan:
docker build -t coverage .
Dan setelah proses build selesai:
docker run -it --rm -v $PWD:/app -w /app coverage:latest php ./vendor/bin/phpunit --coverage-html ./reports/php/coverage
Sekarang Anda tahu Kung Fu! Silakan buka file ./reports/php/coverage/index.html di browser Anda!
Mari kita sambungkan kelas Plugin
kita ke plugin. Sebelum kita benar-benar melakukan pengujian, saya hanya akan menunjukkan cara mendeklarasikan bagian kode Anda agar tidak diuji.
@codeCoverageIgnore
Ini adalah salah satu anotasi penting yang tersedia. Kita akan membahasnya lagi nanti, tapi pertama-tama:
Jalankan pengujian unit dengan laporan cakupan lagi!
Anda mungkin memperhatikan kolom CRAP
di laporan liputan. CRAP adalah singkatan dari change risk anti-patterns . Ini menunjukkan betapa berisikonya perubahan kode dalam suatu kelas atau metode. Anda dapat menurunkan risiko (dan juga indeks) dengan kode yang tidak terlalu rumit dan cakupan penuh dengan pengujian.
Mari kita mulai menguji sesuatu. Tapi apa? Masih belum ada fungsionalitas lebih lanjut yang perlu diuji.
Inilah TDD (Test Driven Development) ke dalam permainan.
Bahkan jika Anda memutuskan untuk tidak menggunakan teknik ini, Anda setidaknya harus tahu apa yang sedang kita bicarakan.
Pertama-tama mari kita buat Test CarTest
yang akan menguji apakah metode get_price
mengembalikan string '€ 14.500'
. Kemudian buat Car
Kelas dan tulis metode get_price
yang memenuhi pengujian. Jangan mulai dengan implementasinya.
Pada titik ini izinkan saya memperkenalkan juga pola pengujian AAA (Arrange Act Assert) yang diterima secara luas di TDD . Ini menjelaskan cara mengatur tes dan sangat mirip dengan GWT (Given When Then) dari BDD (Behavior-driven Development).
Anda dapat menguji kelas Anda jika mereka mengeluarkan pengecualian dalam kondisi tertentu. Sekarang mari kita terapkan metode get_price
.
Cukup buat kelas Registry
yang menetapkan nilai campuran sebagai item bernama dalam array internal. Gunakan metode set()
atau metode ajaib __set()
untuk ini. Pertama-tama asumsikan bahwa kita dapat meneruskan objek JSON ke kelas Car
kita. Ini akan memberikan nilai lebih pada kelas kita.
Metode lain get
atau __get()
harus memeriksa apakah item dengan tertentu ada dan mengembalikannya jika berhasil. Jika tidak ada item seperti itu, lemparkan OutOfBoundsException
. Sekarang tulis konstruktor yang menangani input JSON dan menyimpan objek dalam member-var data
. Metode get_price
- harus mengambil harga dari var data
dan menangani keluaran yang diformat.
Periksa cabang langkah-10 jika Anda kesulitan menulis kode! price
variabel harus berupa bilangan bulat. Ini mungkin bukan masalah saat ini karena Anda dapat menggunakan fungsi PHP number_format()
untuk membuat keluaran yang benar. Namun dalam instalasi WordPress Anda mengharapkan pengaturan lokal, ke it_IT
(Italia) misalnya.
Cara memformat angka di WordPress yang benar adalah dengan menggunakan fungsi number_format_i18n()
.
Jadi mari kita ubah itu dan lihat apa yang terjadi:
Error: Call to undefined function wcctanumber_format_i18n()
Kami akan memperbaikinya sebentar lagi, tapi mari kita persiapkan ini dulu. Brain Monkey menggunakan setUp()
dan tearDown()
yang disediakan oleh PHPUnit . Anda dapat mengganti metode tersebut. Mari kita buat TestCase
khusus - beri nama WcctaCase
- yang dapat kita perluas karena kita mungkin akan melakukan ini di setiap kelas pengujian.
Sekarang mari sertakan namespace untuk pengujian di bagian autoload-dev:
"autoload-dev": {
"psr-4": {
"tests\wccta\": "tests/wccta"
}
},
Terakhir, mari kita ubah induk kelas pengujian kita.
class CarTest extends WcctaTestCase { // ... }
Kami siap untuk meniru fungsi WordPress pertama kami dengan
Functionsexpect( $name_of_function )->andReturn( $value );
Menulis tes hanya untuk satu ekspektasi sepertinya terlalu merepotkan. Bagaimana jika Anda ingin menguji nilai yang berbeda?
Penyedia data untuk menyelamatkan. Saya sudah membicarakan tentang anotasi di langkah 5. Yang ini juga sangat berguna:
@dataprovider method_that_returns_data
Lihat contoh saya. getData
mengembalikan array dari array. Masing-masing array ini berisi 3 nilai. Metode test_getPrice
kami tidak hanya dapat menerima penyedia data dengan anotasi, tetapi juga dapat menentukan var input sebagai parameter.
Anda dapat menguji kelas Anda jika mereka mengeluarkan pengecualian dalam kondisi tertentu.
Cukup buat kelas Registry
yang menetapkan nilai campuran sebagai item bernama dalam array internal. Gunakan metode set()
atau metode ajaib __set()
untuk ini.
Metode lain get
atau __get()
harus memeriksa apakah item dengan kunci tertentu ada dan mengembalikannya jika berhasil. Jika tidak ada item seperti itu, lemparkan OutOfBoundsException
.
Periksa cabang langkah-10 jika Anda kesulitan menulis kode!
Langkah terakhir membawa kami ke Pabrik . Apa itu pabrik? Terkadang Anda membuat fungsi atau metode yang hanya menyembunyikan proses kompleks untuk membuat objek tertentu. Dan terkadang Anda harus memutuskan jenis objek yang ingin Anda buat.
Di plugin WordPress saya lebih suka menambahkan kait di pabrik ke objek. Ada plugin yang menambahkan kait di konstruktor kelas. Ini bukan hal yang baik (terutama ketika Anda masih menguji cara klasik -menciptakan lingkungan yang lengkap dengan WordPress yang aktif dan berjalan).
Mari kita buat kelas Factory
dengan fungsi statis bernama create
. Metode ini harus mengembalikan objek Car
. Tapi mari kita memfaktorkan ulang konstruktor Car
sehingga ia mengharapkan objek dan tidak ada string JSON. Kami akan melakukan ini dalam metode pembuatan kelas Factory
sebagai gantinya.
Uji plugin Anda sekarang dengan composer test
dan Anda akan melihat beberapa kesalahan:
TypeError: Argument 1 passed to wcctaCar::__construct() must be an object, string given, called in ...
Kita harus memperbaiki tes kita juga...
Bagus sekali! Mari kita buat tes untuk Pabrik kita. Kami akan membiarkan metode tersebut tanpa konten apa pun untuk saat ini. Jalankan tes lagi!
There was 1 risky test:
1) testswcctaFactoryTest::test_create
This test did not perform any assertions
Tesnya lulus tetapi Anda mendapat pesan bahwa ada tes yang berisiko. Omong-omong: Beri nama fungsi test_create
cukup create
dan gunakan anotasi @test
. Saya yakin penggunaan anotasi tersebut bergantung pada selera pribadi Anda!
Sekarang kita akan mendalami hal ini lebih dalam.
Buat antarmuka FooterInterface
yang mendefinisikan info
metode publik yang tidak mengharapkan nilai kembalian apa pun. Menerapkan antarmuka di Car
, info
dapat - misalnya - menampilkan pesan lucu.
Tentukan tipe pengembalian FooterInterface
untuk metode create
Factory
dan tambahkan metode info
Car
ke WordPress-Action wp_footer
.
Sekarang mari kita uji ini di FactoryTest
. Setidaknya ada dua cara untuk mengujinya dengan benar. Gunakan has_action atau ActionsexpectAdded()
. Pengujian untuk filter akan serupa dan dijelaskan dengan baik di halaman tertaut.
Periksa apakah composer test
masih lulus semua tes.
Bagaimana liputannya saat ini? Jalankan composer coverage
dan periksa output yang dihasilkan.
info
-metode kelas Car
kami tidak tercakup dalam tes apa pun. Tapi bisakah kita menguji keluaran suatu metode?
Ternyata cukup mudah dengan ExpectOutputString.
Mari kita rayakan apa yang kita pelajari!
Buat kelas Locale
yang memiliki metode publik get
yang mengembalikan get_locale()
. Kecualikan metode ini dari cakupan!
Sekarang buat konstruktor di kelas Plugin
kami yang menerima Locale
-instance dan simpan di member-var $this->locale
. Kemudian buat metode get_region_code
yang mengembalikan nilai $this->locale->get()
. Ah, dan hapus metode is_loaded
. ;)
Dalam pengujian kami, kami dapat membuat objek bertipe Locale
, meniru fungsi WordPress get_locale
dan meneruskannya ke konstruktor Plugin
! Tapi saya ingin menggunakan Mocker di sini:
public function test_get_region_code() {
$code = 'it_IT';
$locale = Mockery::mock( Locale::class );
$locale->shouldReceive( 'get' )->andReturn( $code );
$sut = new Plugin( $locale );
$this->assertEquals( $code, $sut->get_region_code() );
}
Sekarang Anda dapat membuat plugin WordPress Anda antipeluru!
Selamat bersenang-senang!