Alur kerja BDD dengan Behat dan Phpspec
() translation by (you can also view the original English article)
Dalam tutorial ini, kita akan melihat dua tool BDD berbeda, Behat dan phpspec, dan melihat bagaimana mereka dapat mendukung Anda dalam proses pengembangan Anda. Belajar BDD dapat membingungkan. Metodologi baru, tool-tool baru dan banyak pertanyaan, seperti "apa yang harus diuji?" dan "tool apa yang digunakan?". Saya berharap bahwa contoh agak sederhana ini akan memberi Anda ide untuk bagaimana Anda dapat memasukkan BDD ke dalam alur kerja Anda sendiri.
Inspirasi saya
Saya terinspirasi untuk menulis tutorial ini oleh Taylor Otwell, pencipta Laravel framework. Beberapa kali, aku telah mendengar Taylor menjelaskan mengapa sebagian besar tidak dilakukannya TDD BDD dengan mengatakan bahwa dia suka untuk pertama merencanakan API kode nya, sebelum benar-benar mulai untuk menerapkannya. Aku telah mendengar ini dari banyak pengembang, dan setiap kali saya berpikir untuk diri sendiri: "Tapi itu adalah kasus penggunaan sempurna untuk TDD/BDD!". Taylor mengatakan bahwa dia suka untuk memetakan API kode nya, dengan menulis kode yang berharap dia punya. Ia akan memulai coding dan tidak akan puas sampai dia telah mencapai API yang tepat. Argumen masuk akal jika Anda hanya menguji/speccing pada tingkat unit, tetapi menggunakan alat seperti Behat, Anda mulai dengan perilaku eksternal perangkat lunak Anda, yang pada dasarnya, sejauh yang saya mengerti, apa yang ingin Taylor capai.
Apa yang akan kita cakup
Dalam tutorial ini, kita akan membangun class loader file konfigurasi sederhana. Kita akan mulai dengan menggunakan pendekatan Taylor dan kemudian beralih ke pendekatan BDD sebagai gantinya. Contoh minimalis, tapi tetap kita harus khawatir tentang fixture, static metode dll, jadi All-in-semua, saya pikir mereka harus cukup untuk menunjukkan bagaimana Behat dan phpspec dapat saling melengkapi.
Disclaimer: Pertama-tama, artikel ini bukanlah panduan memulai. Ini mengasumsikan pengetahuan dasar BDD, Behat dan phpspec. Anda telah mungkin sudah melihat ke dalam tool ini, tetapi masih berjuang dengan cara benar-benar menggunakan mereka dalam alur kerja harian Anda. Jika Anda ingin mempelajari lagi pada phpspec, lihatlah saya memulai tutorial. Kedua dari semua, saya menggunakan Taylor Otwell sebagai contoh. Aku tidak tahu apa-apa tentang bagaimana bekerja Taylor, selain apa yang aku mendengar dia berkata dalam podcast dll. Saya menggunakan dia sebagai contoh karena ia adalah pengembang yang mengagumkan (ia dibuat Laravel!) dan karena ia terkenal. Saya mungkin juga telah menggunakan orang lain, karena sebagian besar pengembang, termasuk saya sendiri, tidak melakukan BDD sepanjang waktu, belum. Juga, saya tidak mengatakan bahwa alur kerja Taylor itu buruk. Saya pikir itu adalah ide cemerlang untuk menaruh beberapa pemikiran ke dalam kode Anda sebelum benar-benar menulis itu. Tutorial ini hanya dimaksudkan untuk menunjukkan BDD untuk melakukan hal ini.
Taylor's Workflow
Mari kita mulai dengan melihat bagaimana Taylor mungkin pergi tentang merancang loader file konfigurasi ini. Taylor mengatakan bahwa dia suka menulis sebuah file teks kosong di editor dan kemudian menulis bagaimana ia ingin pengembang untuk dapat berinteraksi dengan kode nya (API). Dalam konteks BDD, ini biasanya disebut sebagai testing external behavior perangkat lunak dan tool seperti Behat bagus untuk ini. Kita akan melihat ini dalam waktu singkat.
Pertama, mungkin Taylor akan membuat keputusan tentang file konfigurasi. Bagaimana mereka harus bekerja? Seperti Laravel, mari kita hanya menggunakan array PHP yang sederhana. Contoh konfigurasi file bisa terlihat seperti ini:
1 |
# config.php |
2 |
|
3 |
<?php
|
4 |
|
5 |
return array( |
6 |
|
7 |
'timezone' => 'UTC', |
8 |
|
9 |
);
|
Selanjutnya, bagaimana harus menggunakan kode yang membuat file konfigurasi ini bekerja? Mari kita melakukan ini dengan cara Taylor dan hanya menulis kode yang kami berharap bisa memiliki:
1 |
$config = Config::load('config.php'); |
2 |
|
3 |
$config->get('timezone'); // returns 'UTC' |
4 |
|
5 |
$config->get('timezone', 'CET'); // returns 'CET' if 'timezone' is not configured |
6 |
|
7 |
$config->set('timezone', 'GMT'); |
8 |
$config->get('timezone'); // returns 'GMT' |
Oke, jadi ini terlihat cukup bagus. Pertama kita memiliki panggilan statis untuk fungsi load()
, diikuti oleh tiga case digunakan:
- Mendapatkan "timezone" dari file konfigurasi.
- Mendapatkan nilai default, jika "timezone" belum dikonfigurasi.
- Mengubah pilihan konfigurasi dengan setting untuk sesuatu yang lain. Kami akan menjelaskan setiap kasus penggunaan, atau skenario, dengan Behat dalam waktu singkat.
Masuk akal untuk menggunakan Behat untuk hal-hal ini. Behat tidak akan memaksa kita untuk membuat keputusan desain - kami memiliki phpspec untuk itu. Kita hanya akan bergerak persyaratan, yang kita baru saja dijelaskan, menjadi fitur Behat untuk memastikan bahwa kita mendapatkan yang benar, ketika kita mulai membangun. Fitur behat kami akan berfungsi sebagai acceptance tes untuk persyaratan kami sehingga untuk berbicara.
Jika Anda melihat kode kita menulis, alasan lain untuk menggunakan Behat, bukan hanya phpspec, adalah static call. Hal ini tidak mudah untuk menguji static meteode, terutama jika Anda hanya menggunakan tool seperti phpspec. Kita akan melihat bagaimana kita bisa pergi tentang hal ini ketika kita memiliki Behat dan phpspec yang tersedia.
Setup
Dengan asumsi Anda menggunakan Composer, mengatur Behat dan phspec adalah super sederhana dua langkah proses.
Pertama, Anda perlu file composer.json
dasar. Ini memasukan Behat dan phpspec, dan menggunakan psr-4 untuk autoload class:
1 |
{
|
2 |
"require-dev": { |
3 |
"behat/behat": "~3.0", |
4 |
"phpspec/phpspec": "~2.0" |
5 |
},
|
6 |
"autoload": { |
7 |
"psr-4": { |
8 |
"": "src/" |
9 |
}
|
10 |
}
|
11 |
}
|
Menjalankan composer install
untuk mengambil dependensi.
phpspec tidak membutuhkan konfigurasi apapun untuk menjalankan, sedangkan Behat membutuhkan untuk menjalankan perintah berikut untuk menghasilkan sebuah scaffold dasar:
1 |
$ vendor/bin/behat --init |
Itu adalah semua yang diperlukan. Ini tidak dapat menjadi alasan Anda untuk tidak melakukan BDD!
Perencanaan fitur dengan Behat
Jadi, sekarang semuanya sudah diatur, kami siap untuk mulai melakukan BDD. Karena melakukan BDD berarti menginstal dan menggunakan Behat dan phpspec, kan?
Sejauh yang saya prihatin, kita sudah mulai melakukan BDD. Kami secara efektif telah menggambarkan perilaku eksternal perangkat lunak kami. Kami "klien" dalam contoh ini adalah pengembang, yang akan berinteraksi dengan kode kita. Dengan "efektif", saya berarti bahwa kami telah menggambarkan perilaku dengan cara yang mereka akan mengerti. Kita bisa mengambil kode yang kita telah digariskan, memasukkannya ke dalam README file, dan setiap PHP pengembang akan memahami penggunaan itu. Jadi ini sebenarnya cukup bagus, tapi aku punya dua hal penting untuk dicatat ini. Pertama-tama, menggambarkan perilaku perangkat lunak menggunakan kode hanya bekerja dalam contoh ini karena "klien" programmer. Biasanya, kami uji sesuatu yang akan digunakan oleh orang-orang yang "normal". Bahasa manusia lebih baik daripada PHP ketika kita ingin berkomunikasi dengan manusia. Kedua dari semua, mengapa tidak mengotomatisasi ini? Aku tidak akan berdebat mengapa ini mungkin ide yang baik.
Itu yang dikatakan, saya pikir mulai menggunakan Behat sekarang akan menjadi keputusan yang masuk akal.
Menggunakan Behat, kami ingin menjelaskan masing-masing skenario yang telah kita dijelaskan di atas. Kami tidak ingin untuk menutupi luas setiap edge case yang terlibat dalam menggunakan perangkat lunak. Kami memiliki phpspec tersedia jika ini harus dibutuhkan untuk memperbaiki bug di sepanjang jalan dll. Saya pikir banyak pengembang, mungkin termasuk Taylor, merasa seperti mereka harus memikirkan semuanya dan memutuskan segala sesuatu sebelum mereka dapat menulis tes dan spesifikasi. Itulah sebabnya mengapa mereka memilih untuk memulai tanpa BDD, karena mereka tidak ingin memutuskan segala sesuatu terlebih dahulu. Hal ini tidak terjadi dengan Behat, karena kita menggambarkan perilaku eksternal dan penggunaan. Untuk menggunakan Behat untuk menggambarkan fitur, kita tidak perlu memutuskan apa-apa lebih dari pada contoh di atas dengan menggunakan file teks mentah. Kita hanya perlu untuk menentukan persyaratan fitur - dalam kasus ini API eksternal class loader file konfigurasi.
Sekarang, mari kita ambil kode PHP di atas dan mengubahnya menjadi Behat fitur, yang menggunakan bahasa Inggris (benar-benar menggunakan bahasa Gherkin).
Membuat file di features/
direktori, disebut config.feature,
dan mengisi skenario berikut:
1 |
Feature: Configuration files |
2 |
In order to configure my application |
3 |
As a developer |
4 |
I need to be able to store configuration options in a file |
5 |
|
6 |
Scenario: Getting a configured option |
7 |
Given there is a configuration file |
8 |
And the option 'timezone' is configured to 'UTC' |
9 |
When I load the configuration file |
10 |
Then I should get 'UTC' as 'timezone' option |
11 |
|
12 |
Scenario: Getting a non-configured option with a default value |
13 |
Given there is a configuration file |
14 |
And the option 'timezone' is not yet configured |
15 |
When I load the configuration file |
16 |
Then I should get default value 'CET' as 'timezone' option |
17 |
|
18 |
Scenario: Setting a configuration option |
19 |
Given there is a configuration file |
20 |
And the option 'timezone' is configured to 'UTC' |
21 |
When I load the configuration file |
22 |
And I set the 'timezone' configuration option to 'GMT' |
23 |
Then I should get 'GMT' as 'timezone' option |
Dalam fitur ini, kami menggambarkan, dari luar, bagaimana "pengembang" akan mampu menyimpan pilihan konfigurasi. Kita tidak peduli tentang perilaku internal - kami akan ketika kita mulai menggunakan phpspec. Asalkan fitur ini berjalan hijau, kita tidak peduli apa yang terjadi di belakang layar.
Mari kita menjalankan Behat dan melihat apa yang dipikirkan fitur:
1 |
$ vendor/bin/behat --dry-run --append-snippets |
Dengan perintah ini, kami memberitahukan Behat untuk menambahkan definisi langkah penting untuk feature context. Aku tidak akan pergi lebih detal tentang Behat, tapi ini menambah sekelompok metode kosong FeatureContext
class kami yang memetakan ke langkah fitur kami di atas.
Sebagai contoh, lihatlah definisi langkah bahwa Behat ditambahkan langkah there is a configuration file
yang kami gunakan sebagai langkah "Given" semua tiga skenario:
1 |
/**
|
2 |
* @Given there is a configuration file
|
3 |
*/
|
4 |
public function thereIsAConfigurationFile() |
5 |
{
|
6 |
throw new PendingException(); |
7 |
}
|
Sekarang, Semua harus kita lakukan adalah untuk mengisi beberapa kode untuk menggambarkan hal ini.
Menulis langkah definisi
Sebelum kita mulai, saya punya dua hal penting untuk membuat tentang definisi langkah:
- Intinya adalah untuk membuktikan bahwa perilaku sekarang, adalah tidak seperti yang kita inginkan menjadi. Setelah itu, kita bisa mulai untuk merancang kode kita, sebagian besar waktu menggunakan phpspec, untuk mendapatkan hijau.
- Implementasi langkah definisi tidak penting - kita hanya perlu sesuatu yang bekerja dan mencapai "1". Kita dapat refactor kemudian.
Jika Anda menjalankan vendor/bin/behat
, Anda akan melihat bahwa semua skenario sekarang telah pending.
Kita mulai dengan langkah Given there is a configuration file.
Kita akan menggunakan fixture file konfigurasi, sehingga kita dapat menggunakan metode statis load()
kemudian. Kita peduli tentang metode statis load()
karena hal itu memberikan kita API Config::load()
, banyak seperti Laravel facade. Langkah ini dapat diterapkan dalam berbagai cara. Untuk sekarang, saya pikir kita harus hanya memastikan bahwa kita memiliki fixture yang tersedia dan bahwa hal itu berisi array:
1 |
/**
|
2 |
* @Given there is a configuration file
|
3 |
*/
|
4 |
public function thereIsAConfigurationFile() |
5 |
{
|
6 |
if ( ! file_exists('fixtures/config.php')) |
7 |
throw new Exception("File 'fixtures/config.php' not found"); |
8 |
|
9 |
$config = include 'fixtures/config.php'; |
10 |
|
11 |
if ( ! is_array($config)) |
12 |
throw new Exception("File 'fixtures/config.php' should contain an array"); |
13 |
}
|
Kita akan menuju ke hijau dengan langkah ini tanpa menerapkan kode apapun selain membuat fixture tersebut. Tujuan dari langkah Given
adalah untuk meletakkan sistem dalam keadaan yang dikenal. Dalam kasus ini, itu berarti memastikan kita memiliki file konfigurasi.
Untuk langkah hijau pertama kami, kita hanya perlu untuk membuat fixture:
1 |
# fixtures/config.php |
2 |
<?php
|
3 |
|
4 |
return array(); |
Selanjutnya, kita memiliki langkah And
, yang dalam kasus ini adalah hanya sebuah alias untuk Given
. Kami ingin memastikan bahwa file konfigurasi berisi pilihan untuk timezone. Sekali lagi, ini hanya berhubungan dengan fixture kami, jadi kami tidak peduli banyak tentang hal itu. Aku membanting kode berikut (hackish) bersama-sama untuk mencapai hal ini:
1 |
/**
|
2 |
* @Given the option :option is configured to :value
|
3 |
*/
|
4 |
public function theOptionIsConfiguredTo($option, $value) |
5 |
{
|
6 |
$config = include 'fixtures/config.php'; |
7 |
|
8 |
if ( ! is_array($config)) $config = []; |
9 |
|
10 |
$config[$option] = $value; |
11 |
|
12 |
$content = "<?php\n\nreturn " . var_export($config, true) . ";\n"; |
13 |
|
14 |
file_put_contents('fixtures/config.php', $content); |
15 |
}
|
Kode di atas tidak cantik, tapi accomplishes apa yang perlu. Hal ini memungkinkan kita memanipulasi fixture kami dari dalam fitur kami. Jika Anda menjalankan Behat, Anda akan melihat bahwa itu menambahkan opsi "timezone" untuk config.php
fixture:
1 |
<?php
|
2 |
|
3 |
return array ( |
4 |
'timezone' => 'UTC', |
5 |
);
|
Sekarang adalah waktu untuk membawa beberapa asli "Taylor kode"! Langkah When I load the configuration file
akan terdiri dari kode yang kita benar-benar peduli tentang. Kami akan membawa beberapa kode dari file teks mentah dari sebelumnya, dan pastikan bahwa itu berjalan:
1 |
/**
|
2 |
* @When I load the configuration file
|
3 |
*/
|
4 |
public function iLoadTheConfigurationFile() |
5 |
{
|
6 |
$this->config = Config::load('fixtures/config.php'); // Taylor! |
7 |
}
|
Menjalankan Behat, tentu ini akan gagal, karena Config
belum ada. Mari kita membawa phpspec untuk menyelamatkannya!
Merancang dengan Phpspec
Ketika kita menjalankan Behat, kita akan mendapatkan kesalahan fatal:
1 |
PHP Fatal error: Class 'Config' not found...
|
Untungnya, kami memiliki phpspec yang tersedia, termasuk Kode Generator yang mengagumkan.
1 |
$ vendor/bin/phpspec desc "Config" |
2 |
Specification for Config created in .../spec/ConfigSpec.php. |
3 |
|
4 |
$ vendor/bin/phpspec run --format=pretty |
5 |
Do you want me to create `Config` for you? y |
6 |
|
7 |
$ vendor/bin/phpspec run --format=pretty |
8 |
|
9 |
Config |
10 |
|
11 |
10 ✔ is initializable |
12 |
|
13 |
|
14 |
1 specs |
15 |
1 examples (1 passed) |
16 |
7ms |
Dengan perintah ini, phpspec dibuat dua file berikut untuk kami:
1 |
spec/ |
2 |
`-- ConfigSpec.php |
3 |
src/ |
4 |
`-- Config.php |
Hal ini membuat kita menyingkirkan kesalahan fatal pertama, tapi Behat masih tidak berjalan:
1 |
PHP Fatal error: Call to undefined method Config::load() in ... |
load()
akan menjadi metode statis dan dengan demikian, adalah tidak mudah specced dengan phpspec. Untuk dua alasan ini adalah OK, meskipun:
- Perilaku metode
load()
yang akan menjadi sangat sederhana. Jika kita membutuhkan lebih kompleksitas nanti, kita dapat mengekstrak logika untuk dapat diuji metode kecil. - Perilaku, seperti untuk sekarang, ditutupi dengan cukup baik oleh Behat. Jika metode tidak memuat file ke dalam sebuah array dengan benar, Behat akan squirk pada kami.
Ini adalah salah satu situasi dimana banyak pengembang akan menabrak dinding. Mereka akan membuang phpspec dan menyimpulkan bahwa itu menyebalkan dan bekerja melawan mereka. Tapi, melihat seberapa baik Behat dan phpspec yang saling melengkapi di sini?
Daripada mencoba untuk mendapatkan 100% cakupan dengan phpspec, mari kita hanya menerapkan fungsi sederhana load()
dan menjadi yakin bahwa ditutupi oleh Behat:
1 |
<?php
|
2 |
|
3 |
class Config |
4 |
{
|
5 |
protected $settings; |
6 |
|
7 |
public function __construct() |
8 |
{
|
9 |
$this->settings = array(); |
10 |
}
|
11 |
|
12 |
public static function load($path) |
13 |
{
|
14 |
$config = new static(); |
15 |
|
16 |
if (file_exists($path)) |
17 |
$config->settings = include $path; |
18 |
|
19 |
return $config; |
20 |
}
|
21 |
}
|
Kami cukup yakin bahwa opsi-opsi konfigurasi kami sekarang dimuat. Jika tidak, sisa langkah kami akan gagal dan kita dapat melihat ke dalam ini lagi.
Membangun fitur dengan iterasi
Kembali ke hijau dengan Behat dan phpspec, kita dapat sekarang melihat langkah fitur berikutnya Then I should get 'UTC' as 'timezone' option.
1 |
/**
|
2 |
* @Then I should get :value as :option option
|
3 |
*/
|
4 |
public function iShouldGetAsOption($value, $option) |
5 |
{
|
6 |
$actual = $this->config->get($option); // Taylor! |
7 |
|
8 |
if ( ! strcmp($value, $actual) == 0) |
9 |
throw new Exception("Expected {$actual} to be '{$option}'."); |
10 |
}
|
Dalam langkah ini kita menulis lebih dari kode yang kami berharap bisa memiliki. Menjalankan Behat meskipun, kita akan melihat bahwa kita tidak memiliki get()
metode yang tersedia:
1 |
PHP Fatal error: Call to undefined method Config::get() in ... |
Saatnya untuk kembali ke phpspec dan menyelesaikan masalah ini.
Pengujian accesor dan mutators, AKA Getter dan setter, adalah hampir seperti itu lama dillemma ayam atau telur. Bagaimana kita dapat menguji get()
metode jika kita belum memiliki set()
metode dan sebaliknya. Bagaimana saya cenderung untuk pergi tentang hal ini adalah untuk hanya menguji keduanya sekaligus. Ini berarti bahwa kita benar-benar akan menerapkan fungsi untuk mengatur pilihan konfigurasi, walaupun kita tidak belum mencapai skenario itu .
Contoh berikut akan melakukannya:
1 |
function it_gets_and_sets_a_configuration_option() |
2 |
{
|
3 |
$this->get('foo')->shouldReturn(null); |
4 |
|
5 |
$this->set('foo', 'bar'); |
6 |
|
7 |
$this->get('foo')->shouldReturn('bar'); |
8 |
}
|
Pertama, kita akan memiliki generator phpspec membantu kita memulai:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
Do you want me to create `Config::get()` for you? y |
3 |
|
4 |
$ vendor/bin/phpspec run --format=pretty |
5 |
Do you want me to create `Config::set()` for you? y |
6 |
|
7 |
$ vendor/bin/phpspec run --format=pretty |
8 |
|
9 |
Config |
10 |
|
11 |
10 ✔ is initializable |
12 |
15 ✘ gets and sets a configuration option |
13 |
expected "bar", but got null.
|
14 |
|
15 |
... |
16 |
|
17 |
1 specs |
18 |
2 examples (1 passed, 1 failed) |
19 |
9ms |
Sekarang, mari kita kembali ke hijau:
1 |
public function get($option) |
2 |
{
|
3 |
if ( ! isset($this->settings[$option])) |
4 |
return null; |
5 |
|
6 |
return $this->settings[$option]; |
7 |
}
|
8 |
|
9 |
public function set($option, $value) |
10 |
{
|
11 |
$this->settings[$option] = $value; |
12 |
}
|
Dan, begitu:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
|
3 |
Config |
4 |
|
5 |
10 ✔ is initializable |
6 |
15 ✔ gets and sets a configuration option |
7 |
|
8 |
|
9 |
1 specs |
10 |
2 examples (2 passed) |
11 |
9ms |
Yang punya jalan panjang. Menjalankan Behat, kita melihat bahwa kita adalah baik ke kedua skenario sekarang. Selanjutnya, kita perlu untuk mengimplementasikan fitur default pilihan, karena get()
hanya mengembalikan null
sekarang.
Langkah fitur pertama serupa dengan yang kita menulis sebelumnya. Bukannya menambahkan pilihan ke array, kita akan unset
itu:
1 |
/**
|
2 |
* @Given the option :option is not yet configured
|
3 |
*/
|
4 |
public function theOptionIsNotYetConfigured($option) |
5 |
{
|
6 |
$config = include 'fixtures/config.php'; |
7 |
|
8 |
if ( ! is_array($config)) $config = []; |
9 |
|
10 |
unset($config[$option]); |
11 |
|
12 |
$content = "<?php\n\nreturn " . var_export($config, true) . ";\n"; |
13 |
|
14 |
file_put_contents('fixtures/config.php', $content); |
15 |
}
|
Hal ini tidak cantik. Saya tahu! Kita pasti bisa refactor itu, karena kami berulang diri kita sendiri, tapi itu bukan cakupan tutorial ini.
Langkah kedua fitur juga tampak akrab, dan sebagian besar adalah copy dan paste dari sebelumnya:
1 |
/**
|
2 |
* @Then I should get default value :default as :option option
|
3 |
*/
|
4 |
public function iShouldGetDefaultValueAsOption($default, $option) |
5 |
{
|
6 |
$actual = $this->config->get($option, $default); // Taylor! |
7 |
|
8 |
if ( ! strcmp($default, $actual) == 0) |
9 |
throw new Exception("Expected {$actual} to be '{$default}'."); |
10 |
}
|
get()
mengembalikan null
. Mari kita melompat ke phpspec dan menulis contoh untuk memecahkan masalah ini:
1 |
function it_gets_a_default_value_when_option_is_not_set() |
2 |
{
|
3 |
$this->get('foo', 'bar')->shouldReturn('bar'); |
4 |
|
5 |
$this->set('foo', 'baz'); |
6 |
|
7 |
$this->get('foo', 'bar')->shouldReturn('baz'); |
8 |
}
|
Pertama, kami memeriksa bahwa kita mendapatkan nilai default jika "pilihan" belum dikonfigurasi. Kedua, kami pastikan bahwa opsi default tidak menimpa opsi konfigurasi.
Pada pandangan pertama, phpspec mungkin tampak seperti berlebihan dalam hal ini, karena kami hampir menguji hal yang sama dengan Behat. Saya ingin menggunakan phpspec untuk spec edge-case meskipun, yang seperti yang tersirat dalam skenario. Dan juga, Generator kode dari phpspec benar-benar hebat. Saya menggunakannya untuk segalanya dan aku mendapati diriku bekerja lebih cepat setiap kali saya menggunakan phpspec.
Sekarang, phpspec menegaskan apa Behat telah mengatakan kepada kami:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
|
3 |
Config |
4 |
|
5 |
10 ✔ is initializable |
6 |
15 ✔ gets and sets a configuration option |
7 |
24 ✘ gets a default value when option is not set
|
8 |
expected "bar", but got null. |
9 |
|
10 |
... |
11 |
|
12 |
1 specs |
13 |
3 examples (2 passed, 1 failed) |
14 |
9ms |
Untuk mendapatkan kembali ke green, kita akan menambahkan "kembali awal" untuk get()
metode:
1 |
public function get($option, $defaultValue = null) |
2 |
{
|
3 |
if ( ! isset($this->settings[$option]) and ! is_null($defaultValue)) |
4 |
return $defaultValue; |
5 |
|
6 |
if ( ! isset($this->settings[$option])) |
7 |
return null; |
8 |
|
9 |
return $this->settings[$option]; |
10 |
}
|
Kami melihat bahwa phpspec sekarang bahagia:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
|
3 |
Config |
4 |
|
5 |
10 ✔ is initializable |
6 |
15 ✔ gets and sets a configuration option |
7 |
24 ✔ gets a default value when option is not set
|
8 |
|
9 |
|
10 |
1 specs |
11 |
3 examples (3 passed) |
12 |
9ms |
Dan begitu adalah Behat, dan kita.
Kami selesai dengan skenario kedua kami dan memiliki satu tersisa untuk pergi. Untuk skenario terakhir, kita hanya perlu menulis definisi untuk dan And I set the 'timezone' configuration option to 'GMT'
langkah:
1 |
/**
|
2 |
* @When I set the :option configuration option to :value
|
3 |
*/
|
4 |
public function iSetTheConfigurationOptionTo($option, $value) |
5 |
{
|
6 |
$this->config->set($option, $value); // Taylor! |
7 |
}
|
Karena kita sudah diimplementasikan set()
method, langkah ini sudah hijau:
1 |
$ vendor/bin/behat
|
2 |
Feature: Configuration files |
3 |
In order to configure my application |
4 |
As a developer |
5 |
I need to be able to store configuration options in a file
|
6 |
|
7 |
Scenario: Getting a configured option # features/config.feature:6
|
8 |
Given there is a configuration file # FeatureContext::thereIsAConfigurationFile()
|
9 |
And the option 'timezone' is configured to 'UTC' # FeatureContext::theOptionIsConfiguredTo() |
10 |
When I load the configuration file # FeatureContext::iLoadTheConfigurationFile()
|
11 |
Then I should get 'UTC' as 'timezone' option # FeatureContext::iShouldGetAsOption() |
12 |
|
13 |
Scenario: Getting a non-configured option with a default value # features/config.feature:12
|
14 |
Given there is a configuration file # FeatureContext::thereIsAConfigurationFile()
|
15 |
And the option 'timezone' is not yet configured # FeatureContext::theOptionIsNotYetConfigured() |
16 |
When I load the configuration file # FeatureContext::iLoadTheConfigurationFile()
|
17 |
Then I should get default value 'CET' as 'timezone' option # FeatureContext::iShouldGetDefaultValueAsOption() |
18 |
|
19 |
Scenario: Setting a configuration option # features/config.feature:18
|
20 |
Given there is a configuration file # FeatureContext::thereIsAConfigurationFile()
|
21 |
And the option 'timezone' is configured to 'UTC' # FeatureContext::theOptionIsConfiguredTo() |
22 |
When I load the configuration file # FeatureContext::iLoadTheConfigurationFile()
|
23 |
And I set the 'timezone' configuration option to 'GMT' # FeatureContext::iSetTheConfigurationOptionTo() |
24 |
Then I should get 'GMT' as 'timezone' option # FeatureContext::iShouldGetAsOption() |
25 |
|
26 |
3 scenarios (3 passed) |
27 |
13 steps (13 passed) |
28 |
0m0.04s (8.92Mb) |
Ringkasan
Semuanya sudah bagus dan hijau, jadi mari kita memiliki ringkasan cepat dan melihat apa yang telah kita capai.
Kami secara efektif telah menggambarkan perilaku eksternal loader file konfigurasi, pertama dengan menggunakan pendekatan Taylor's, dan kemudian dengan menggunakan pendekatan BDD tradisional. Selanjutnya, kami telah mengimplementasikan fitur, menggunakan phpspec untuk merancang dan menggambarkan perilaku internal. Contoh kami telah bekerja pada ini cukup sederhana, tetapi kita telah membahas dasar-dasar. Jika kita membutuhkan lebih kompleksitas, kita dapat melanjutkan apa yang sudah kita miliki . Menggunakan BDD, kami memiliki setidaknya tiga pilihan:
- Jika kita mengamati bug atau perlu mengubah beberapa internal perangkat lunak kami, kami dapat menjelaskan yang menggunakan phpspec. Menulis contoh gagal yang menampilkan bug dan menulis kode yang diperlukan untuk sampai ke hijau.
- Jika kita perlu menambahkan kasus penggunaan baru untuk apa yang kita miliki, kita dapat menambahkan sebuah skenario untuk
config.feature.
Kita dapat kemudian iteratively bekerja jalan melalui setiap langkah, menggunakan Behat dan phpspec. - Jika kita perlu untuk mengimplementasikan fitur baru, seperti mendukung YAML file konfigurasi, kita dapat menulis fitur baru dan memulai lagi, menggunakan pendekatan kami telah digunakan selama tutorial ini.
Dengan konfigurasi dasar ini, kami memiliki tidak ada alasan untuk tidak menulis gagal tes atau spec, sebelum kita menulis kode kita. Apa yang telah ktia banguan sekarang dicover oleh tes, yang akan membuatnya jauh lebih mudah untuk bekerja dengan itu nantinya. Tambahkan ke bahwa, bahwa kode kita juga sepenuhnya didokumentasikan. Kasus penggunaan yang dijelaskan dalam bahasa Inggris dan cara kerja internal yang dijelaskan dalam spesifikasi kami. Dua hal ini akan membuatnya mudah bagi pengembang lain untuk memahami dan bekerja dengan basis kode.
Itu adalah harapan saya bahwa tutorial ini membantu Anda untuk lebih memahami bagaimana BDD dapat digunakan dalam konteks PHP, dengan Behat dan phpspec. Jika Anda memiliki pertanyaan atau komentar, silahkan post di bawah ini di bagian komentar.
Terima kasih sudah membaca!