Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. BDD
Code

Memahami PhpSpec

by
Difficulty:AdvancedLength:LongLanguages:

Malay (Melayu) translation by Imran bin Hushain (you can also view the original English article)

Jika anda membandingkan PhpSpec dengan rangka kerja ujian yang lain, anda akan mendapati bahawa ini adalah alat yang sangat canggih dan penuh pendapat. Salah satu sebab untuk ini, ialah PhpSpec bukan kerangka ujian yang anda sudah tahu.

Sebaliknya, ia adalah alat reka bentuk yang membantu untuk menggambarkan kelakuan perisian. Kesan sampingan menerangkan perilaku perisian dengan PhpSpec, adalah bahawa anda akan berakhir dengan spesifikasi yang juga akan berfungsi sebagai ujian selepas itu.

Dalam artikel ini, kami akan melihat hud PhpSpec dan cuba mendapatkan pemahaman yang lebih mendalam tentang cara ia berfungsi dan cara menggunakannya.

Sekiranya anda ingin menggilap phpspec, lihat tutorial saya bermula .

Dalam artikel ini ...

  • Jelajah Cepat Dari PhpSpec Dalaman
  • Perbezaan Antara TDD dan BDD
  • Bagaimana PhpSpec adalah berbeza (dengan PHPUnit)
  • PhpSpec: Alat Reka Bentuk

Jelajah Cepat Dari PhpSpec Dalaman

Mari mulakan dengan melihat beberapa konsep dan kelas utama yang membentuk PhpSpec.

Memahami $this

Memahami $this merujuk kepada apada adalah kunci untuk memahami bagaimana PhpSpec berbeza dengan alat lain. Pada asasnya, $this merujuk kepada contoh sebenar kelas apabila diuji. Mari kita meneroka ini lebih sedikit untuk lebih memahami apa yang kita maksudkan.

Pertama sekali, kita memerlukan spesifikasi dan kelas untuk dimainkan. Seperti yang anda tahu, penjana PhpSpec menjadikannya sangat mudah untuk kami:

Seterusnya, buka fail spec yang dihasilkan dan marilah kita cuba mendapatkan sedikit maklumat mengenai $this :

get_class () mengembalikan nama kelas objek yang diberikan. Dalam kes ini, kita hanya membuang $this : di sana untuk melihat hasilnya:

Okay, jadi tidak terlalu mengejutkan, get_class() memberitahu kami $this adalah contoh spec\Suhm\HelloWorldSpec . Ini masuk akal kerana, selepas semua, ini hanyalah kod PHP lama biasa. Sekiranya kita menggunakan get_parent_class () , kami akan mendapat PhpSpec\ObjectBehavior , kerana spec kami memperluaskan kelas ini.

Ingat, saya baru memberitahu anda bahawa $this sebenarnya dipanggil kelas yang diuji, yang akan menjadi Suhm\HelloWorld dalam kes kita? Seperti yang anda lihat, nilai pulangan get_class($this) bertentangan dengan $this-> shouldHaveType('Suhm\HelloWorld');

Mari cuba sesuatu yang lain:

Dengan kod di atas, kami cuba memanggil kaedah bernama dumpThis() dalam contoh HelloWorld . Kami mengaitkan ungkapan untuk kaedah panggilan, menjangkakan nilai pulangan fungsi menjadi rentetan yang mengandungi "spec\Suhm\HelloWorldSpec" . Ini adalah nilai pulangan get_class() dalam baris di atas

Sekali lagi, penjana PhpSpec dapat membantu kami dengan beberapa perancah:

Mari kita cuba memanggil get_class() dari dalam dumpThis() juga:

Sekali lagi, tidak hairanlah, kita dapat:

Sepertinya kita terlepas sesuatu di sini. Saya mula dengan mengatakan $this tidak merujuk kepada apa yang anda fikirkan, tetapi sejauh ini eksperimen kami tidak menunjukkan apa-apa yang tidak dijangka. Kecuali satu perkara: Bagaimana kita boleh memanggil $this->dumpThis() sebelum ia wujud tanpa PHP mencecah kita?

Untuk memahami ini, kita perlu pergi lebih jauh ke dalam kod sumber PhpSpec. Jika anda mahu melihat sendiri, anda boleh membaca kod di GitHub .

Lihat kod berikut dari src/PhpSpec/ObjectBehavior.php (kelas yang memperluaskan spesifikasi kami):

Komen itu memberi kebanyakan: "Proxy semua panggilan ke subjek PhpSpec" . Kaedah PHP __call adalah kaedah ajaib yang dipanggil secara automatik apabila kaedah tidak dapat diakses (atau tidak hadir).

Ini bermakna apabila kita cuba memanggil $this->dumpThis() "Semua panggilan Proksi ke subjek PhpSpec". Sekiranya anda melihat kod tersebut, anda dapat melihat bahawa kaedah panggilan itu diisytiharkan kepada $this->object . (Perkara yang sama berlaku untuk harta benda dalam contoh kita, semuanya diisytiharkan dengan subjek, menggunakan kaedah sihir lain. Lihat sumber untuk mengetahui)

Mari kita berunding tentang get_class() sekali lagi dan lihat apa yang hendak dikatakan mengenai $this->object :

Dan lihat apa yang kita dapat:

Lebih banyak mengenai Subyek

Subyek adalah pembalut dan pelaksanaan PhpSpec\Wrapper\WrapperInterface . Ini adalah bahagian teras PhpSpec dan membolehkan semua sihir [nampaknya] bahawa rangka boleh dilakukan. Ia membungkus contoh kelas yang kami uji, jadi kami boleh melakukan perkara seperti kaedah panggilan dan sifat yang tidak wujud dan menetapkan jangkaan.

Seperti yang disebutkan, PhpSpec sangat menilai tentang bagaimana anda harus menulis dan mentakrifkan kod anda. Satu peta spec untuk satu kelas. Anda hanya mempunyai satu subjek setiap spec, yang PhpSpec akan bungkus dengan teliti untuk anda. Perkara penting yang perlu diperhatikan ialah ini membolehkan anda menggunakan $this seolah-olah ia merupakan contoh yang benar dan menjadikannya sangat mudah dibaca dan bermakna.

PhpSpec mengandungi Wrapper yang mengurus Subject instantiating. Ia membungkus Subject dengan objek yang sebenarnya kita tentukan. Oleh sebab Subject melaksanakan WrapperInterface , ia mesti mempunyai kaedah getWrappedObject() yang memberikan kita akses kepada objek tersebut. Ini adalah contoh objek yang kami cari sebelumnya dengan get_class () .

Mari cuba lagi:

Dan demikianlah:

Walaupun banyak perkara berlaku di belakang tabir, pada akhirnya kami masih bekerja dengan contoh objek sebenar Suhm\HelloWorld . Semua baik-baik saja.

Sebelum ini, apabila kami memanggil $this-> dumpThis() , kami mengetahui bagaimana panggilan itu disokong dengan Subject . Kami juga belajar bahawa Subject hanya pembungkus dan bukan objek sebenar.

Dengan pengetahuan ini, adalah jelas bahawa kita tidak boleh memanggil dumpThis() dalam Subject tanpa sebarang kaedah sihir yang lain. Subject mempunyai kaedah __call() juga:

Kaedah ini melakukan satu daripada dua perkara. Pertama, ia memeriksa sama ada nama kaedah bermula dengan 'mesti'. Sekiranya, ini adalah jangkaan, dan panggilan itu diwakilkan kepada kaedah yang dipanggil callExpectation() . Jika tidak, panggilan itu diwakilkan kepada PhpSpec\Wrapper\Subject\Caller .

Kami akan mengabaikannya Caller sekarang. Iai juga mengandungi objek dibungkus dan tahu cara memanggil kaedah-kaedah di atasnya. Caller mengembalikan contoh yang dibungkus apabila ia memanggil kaedah pada subjek, yang membolehkan kita untuk merancang jangkaan terhadap kaedah, seperti yang kita lakukan dengan dumpThis () .

Sebaliknya, mari kita lihat kaedah callExpectation() :

Kaedah ini bertanggungjawab untuk membina PhpSpec\Wrapper\Subject\Expectation\ExpectationInterface . Ia menentukan kaedah match() di mana callExpectation() dipanggil untuk memeriksa jangkaan. Terdapat empat jenis jangkaan: Positive , Negative , PositiveThrow dan NegativeThrow . Setiap harapan ini mengandungi PhpSpec\Matcher\MatcherInterface bagi kaedah match() Mari kita lihat pada perlawanan seterusnya.

Penyokong

Pencocokan adalah apa yang kita gunakan untuk menentukan kelakuan objek kita. Setiap kali kita menulis should... atau shouldnot... , kita menggunakan shouldnot... . Anda boleh mencari senarai lengkap pemadanan PhpSpec di blog peribadi saya .

Terdapat banyak perlawanan yang disertakan dengan PhpSpec, yang semuanya memanjangkan kelas PhpSpec\Matcher\BasicMatcher , yang melaksanakan MatcherInterface . Bagaimanakah kerja padanan itu agak lurus ke hadapan. Mari lihat bersama dan saya menggalakkan anda untuk melihat Kod Sumber juga.

Sebagai contoh, mari lihat kod ini dari IdentityMatcher :

Kaedah supports() didiktekan oleh MatcherInterface . Dalam kes ini, empat alias ditakrifkan sebagai matcher dalam array $keywords . Ini akan membenarkan shouldReturn() untuk menyokong shouldReturn() , shouldBe() , shouldEqual() atau shouldBeEqualTo() , atau shouldNotReturn() , shouldNotBe() , shouldNotEqual() atau shouldNotBeEqualTo() .

Dari BasicMatcher , dua kaedah diwarisi: positiveMatch() dan negativeMatch () . Mereka kelihatan seperti ini:

Kaedah positifMatch positiveMatch() melemparkan pengecualian jika kaedah matches() (kaedah abstrak yang mesti dilaksanakan oleh pesaing) kembali false . Kaedah negativeMatch() berfungsi dengan cara yang bertentangan. Kaedah matches() untuk IdentityMatcher menggunakan operator === untuk membandingkan $subject dengan argumen yang diberikan dalam kaedah pencocokan:

Kita boleh menggunakan penyokong seperti ini:

Yang akhirnya akan memanggil negativeMatch() dan pastikan matches() mengembalikan palsu

Lihat beberapa pertandingan lain dan lihat apa yang mereka lakukan!

Janji lebih ajaib

Sebelum menamatkan tur dalaman PhpSpec yang singkat ini, mari kita lihat satu lagi keajaiban:

Dengan menambahkan jenis parameter $object tersirat kepada contoh kami, PhpSpec secara automatik akan menggunakan pantulan untuk memasukkan contoh kelas untuk digunakan. Tetapi dengan perkara yang kita lihat, adakah kita benar-benar percaya bahawa kita boleh mempunyai contoh StdClass ? Mari rujuk kembali get_class() :

Ia tidak. Daripada StdClass , kami mendapat PhpSpec\Wrapper\Collaborator . Apa ini?

Seperti Subject , Collaborator adalah WrapperInterface dan pelaksanaan WrapperInterface . Ia membungkus contoh \Prophecy\Prophecy\ObjectProphecy , yang berasal dari Nubuatan , kerangka yang mengejutkan yang datang bersama-sama dengan PhpSpec. Daripada contoh StdClass, PhpSpec memberi kita satu contoh. Ini menjadikan mudah mengejek dengan PhpSpec dan membolehkan kami menambahkan janji kepada objek kami seperti ini:

Dengan lawatan pantas bahagian dalaman PhpSpec ini, saya harap anda melihat bahawa ini lebih daripada sekadar satu rangka kerja ujian yang mudah.

Perbezaan Antara TDD Dan BDD

PhpSpec adalah alat untuk melakukan SpecBDD, jadi untuk mendapatkan pemahaman yang lebih baik, mari kita melihat perbezaan antara pembangunan didorong ujian (TDD) dan pembangunan didorong tingkah laku (BDD). Selepas itu, kita akan melihat sepintas lalu bagaimana PhpSpec berbeza dengan alat lain seperti PHPUnit.

TDD adalah konsep membiarkan reka bentuk kod pemacu automatik dan pelaksanaan ujian automatik. Dengan menulis ujian kecil untuk setiap ciri, sebelum benar-benar melaksanakannya, apabila kami mendapat ujian lulus, kami tahu bahawa kod kami memenuhi ciri khusus tersebut. Dengan ujian yang lulus, selepas refactoring, kami menghentikan pengekodan dan menulis ujian seterusnya. Mantra beliau adalah "merah", "hijau", "refactor"!

BDD adalah dari - dan sangat serupa dengan - TDD. Secara jujur, itu adalah kata-kata, yang penting kerana ia boleh mengubah cara kita berfikir sebagai pemaju. Di mana TDD bercakap mengenai ujian, BDD bercakap tentang menggambarkan tingkah laku.

Dengan TDD, kami menumpukan pada mengesahkan bahawa kod kami sepadan dengan cara kami mengharapkan ia berfungsi, sedangkan dengan BDD, kami menumpukan pada mengesahkan bahawa kod kami adalah apa yang kami inginkan. Sebab utama kemunculan BDD, sebagai alternatif kepada TDD, adalah untuk mengelakkan penggunaan perkataan "test". Dengan BDD, kami tidak begitu berminat untuk menguji penggunaan kod kami, kami lebih berminat untuk menguji apa yang dilakukannya (tingkah lakunya). Apabila kita melakukan BDD, bukan TDD, kita mempunyai cerita dan spesifikasi. Ini membuat ujian tradisional berlebihan.

Cerita dan spesifikasi sangat berkait rapat dengan jangkaan pihak berkepentingan projek. Tulis cerita (dengan alat seperti Behat ), akan berlaku bersama dengan pihak berkepentingan atau ahli domain. Cerita-cerita ini termasuk tingkah laku luar Kami menggunakan spesifikasi untuk merekabentuk tingkah laku dalaman yang diperlukan untuk memenuhi langkah-langkah cerita. Setiap langkah dalam cerita mungkin memerlukan beberapa lelaran dengan spesifikasi pelaksanaan penulisan dan kod, sebelum ia memuaskan.  Kisah kami, bersama dengan spesifikasi kami, membantu kami memastikan bahawa bukan sahaja kami membina perkara-perkara yang berfungsi, tetapi juga perkara yang betul. Oleh itu, BDD mempunyai banyak kaitan dengan komunikasi.

Bagaimanakah PhpSpec Differ from PHPUnit?

Beberapa bulan yang lalu, ahli penting komuniti PHP, Mathias Verraes, mencatatkan " rangka kerja ujian unit Tweet " di Twitter. Titik sepadan dengan kod sumber bingkai ujian unit berfungsi menjadi tweet tunggal. Seperti yang anda dapat lihat dari teras ini, kod ini benar-benar berfungsi, dan membolehkan anda menulis ujian unit asas. Konsep pengujian unit sebenarnya agak mudah: periksa jenis pernyataan dan beritahu pengguna hasilnya.

Sudah tentu, kebanyakan kerangka pengujian, seperti PHPUnit, jauh lebih maju, dan boleh melakukan lebih daripada sekadar kerangka Mathias, tetapi masih menunjuk ke titik: anda mengesahkan sesuatu dan kemudian frame anda menjalankan pernyataan itu untuk anda.

Mari kita ambil ujian PHPUnit yang sangat asas:

Bolehkah anda menulis pelaksanaan yang sangat mudah dalam rangka kerja ujian yang dapat menjalankan ujian ini? Saya cukup yakin bahawa jawapannya adalah "ya" yang boleh anda lakukan itu. Lagipun, satu-satunya perkara yang perlu dilakukan ialah menilai nilai kepada true dan membuang pengecualian jika gagal. Pada dasarnya, apa yang berlaku sebenarnya agak lurus ke hadapan.

Jadi bagaimana PhpSpec berbeza? Pertama sekali, PhpSpec bukanlah alat ujian. Menguji kod anda bukan matlamat utama PhpSpec, tetapi ia adalah kesan sampingan jika anda menggunakannya untuk merancang perisian anda dengan menambahkan spesifikasi untuk tingkah laku (BDD) secara berperingkat.

Kedua, saya fikir bahagian di atas sepatutnya menjelaskan bagaimana PhpSpec adalah berbeza. Walau bagaimanapun, mari bandingkan kod tertentu:

Kerana PhpSpec sangat berpandangan tinggi dan membuat beberapa kenyataan tentang bagaimana kod kami direka, ia memberi kami cara yang sangat mudah untuk menerangkan kod kami. Sebaliknya, PHPUnit tidak membuat sebarang kenyataan terhadap kod kami dan marilah kita melakukan apa yang kami mahukan. Pada dasarnya, semua PHPUnit melakukannya untuk kita dalam contoh ini, adalah untuk menjalankannya $object terhadap pengendali instanceof .

Walaupun PHPUnit mungkin kelihatan lebih mudah untuk memulakan (saya tidak fikir begitu), jika anda tidak berhati-hati, anda boleh dengan mudah terjebak dalam reka bentuk yang buruk dan perangkap seni bina kerana ia membolehkan anda melakukan apa saja. Yang mengatakan, PHPUnit masih boleh menjadi hebat untuk banyak kes penggunaan, tetapi ini bukan alat reka bentuk seperti PhpSpec. Tiada panduan - anda mesti tahu apa yang anda lakukan.

PhpSpec: Alat Reka Bentuk

Dari Laman Web PhpSpec , kami dapat mengetahui bahawa PhpSpec adalah:

Alat Php untuk memandu reka bentuk yang menghasilkan spesifikasi.

Katakan saya sekali lagi: PhpSpec bukan rangka kerja ujian. Ini adalah alat pembangunan. Pereka perisian. Ini bukan kerangka pernyataan mudah yang membandingkan nilai-nilai dan membuang pengecualian. Ini adalah alat yang membantu kami dalam merekabentuk dan membina kod buatan. Ia memerlukan kita memikirkan struktur kod kami dan menggunakan corak seni bina tertentu, di mana satu peta kelas menjadi spesifikasi. Jika anda melanggar prinsip tanggungjawab tunggal dan perlu mengejek sesuatu, anda tidak dibenarkan berbuat demikian.

Tahniah!

Oh! Dan akhirnya, = kerana PhpSpec sendiri adalah spec'ed, saya cadangkan anda pergi ke GitHub dan jelajahi sumber untuk mengetahui lebih lanjut .

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.