Perkenalan BDD (Behavior Driven Development)
oleh: Dan North
(disadur dari “Introducing BDD”)
Saya dulu mempunyai masalah. Sementara menggunakan dan mengajar praktek-praktek Agile seperti test-driven development (TDD) pada proyek-proyek di lingkungan yang berbeda-beda, saya selalu menjumpai kebingungan dan kesalahpahaman. Para programmer selalu ingin mengetahui mulai dari mana, apa yg ditest dan apa yg tidak, seberapa banyak yg ditest sekaligus, test-testnya harus diberi nama apa, dan bagaimana untuk mengerti mengapa sebuah test gagal.
Semakin dalam saya memasuki TDD, semakin saya merasa bahwa perjalanan saya semakin bukan proses “wax-on, wax-off” (dari Karate Kid) untuk menjadi ahli tapi seperti perjalanan ke gang gelap. Saya ingat pernah berpikir “Seandainya seseorang memberi tahu saya tentang itu!” lebih sering dibanding “Wow, sebuah pintu terbuka.” Saya memutuskan bahwa pasti bisa menyajikan TDD dalam sebuah cara yang langsung ke hal-hal yang bagus dan menghindari semua perangkap-perangkapnya.
Jawaban saya adalah behaviour-driven development (BDD). Ini muncul dari praktek-praktek agile yg teruji dan didesign untuk membuat praktek-praktek ini lebih mudah diakses dan efektif untuk tim-tim yang baru mengenal pengerjaan software secara agile. Setelah beberapa waktu, BDD telah tumbuh mencakup gambaran yang lebih luas dari analisa agile dan acceptance testing otomatis.
Nama method test harus dalam bentuk kalimat-kalimat
Momen “Aha!” pertama saya muncul saat saya ditunjukan sebuah program sederhana bernama agiledox, yang ditulis oleh rekan saya, Chris Stevenson. Program ini menganalisa kelas test JUnit dan menuliskan nama-nama method yg ada menjadi kalimat biasa, jadi sebuah test case yg terlihat seperti ini:
public class CustomerLookupTest extends TestCase { testFindsCustomerById() { ... } testFailsForDuplicateCustomers() { ... } ... }
ditulis menjadi seperti ini:
CustomerLookup - finds customer by id - fails for duplicate customers - ...
Kata “test” dihilangkan dari nama kelas dan nama method, dan nama method dalam camel-case diubah menjadi text biasa. Hanya itu saja yg dilakukannya, tapi efeknya luar biasa.
Para developer menemukan bahwa paling tidak hal ini bisa menjadi semacam dokumentasi untuk mereka, jadi mereka mulai menulis test method menggunakan kalimat yang benar. Lebih dari itu, mereka menemukan bahwa saat mereka menulis nama method dalam bahasa domain bisnis, dokumen-dokumen yg dihasilkan lebih masuk akal bagi para bisnis user, para analis, dan para tester.
Sebuah template kalimat sederhana menjaga metode-metode test terfokus
Lalu saya menemukan kaidah menulis nama-nama metode tes dengan diawali kata “should” (harus). Kaidah kalimat ini – Sebuah class harus melakukan sesuatu – berarti anda hanya bisa mendefinisikan sebuah tes untuk kelas tersebut. Ini membuat anda tetap terfokus. Jika anda menemukan anda menulis sebuah tes yang memiliki nama yang tidak cocok dengan kaidah ini, itu artinya “behavior” (tingkah laku) nya tergolong di tempat lain.
Contohnya, saya tadi menulis sebuah kelas yang memvalidasi input dari sebuah halaman. Kebanyakan dari field-field formnya adalah detil klien biasa – nama depan, nama belakang, dll. – tapi kemudian ada isian untuk tanggal lahir dan umur. Saya mulai dengan menulis sebuah kelas ClientDetailsValidatorTest dengan metode-metode seperti testShouldFailForMissingSurname dan testShouldFailForMissingTitle.
Lalu saya mulai menghitung umur dan memasuki sebuah dunia aturan bisnis yang rumit: Bagaimana bila umur dan tanggal lahir dimasukan tapi tidak cocok? Bagaimana bila tanggal lahirnya hari ini? Bagaimana saya menghitung umur bila saya hanya memilik tanggal lahir? Saya mulai menulis nama-nama metode tes yang semakin rumit untuk mendeskripsikan behavior ini, jadi saya mempertimbangkan untuk melakukannya di tempat yang lain. Hal ini menuntun saya untuk menulis sebuah kelas baru yang saya sebut AgeCalculator, dengan kelas tes AgeCalculatorTest. Semua perhitungan umur dipindahkan ke kelas ini, jadi validatornya hanya perlu sebuah tes untuk perhitungan umur untuk memastikan interaksinya dengan kelas perhitungan ini.
Bila sebuah kelas melakukan lebih dari satu hal, saya biasanya melihatnya sebagai sebuah indikasi bahwa saya harus membuat kelas-kelas lain untuk melakukan beberapa pekerjaannya. Saya membuat sebuah service baru sebagai interface yg mengutarakan apa yang dilakukannya, dan saya mempas servis ini melalui konstruktor kelas tersebut:
public class ClientDetailsValidator { private final AgeCalculator ageCalc; public ClientDetailsValidator(AgeCalculator ageCalc) { this.ageCalc = ageCalc; } }
Gaya penyambungan objek-objek ini, juga diketahui sebagai dependency injection, terlebih berguna dalam hubungannya dengan mocks.
Sebuah nama tes yg expresif sangat berguna saat sebuah tes gagal
Setelah beberapa lama, saya menemukan bahwa bila saya sedang mengubah kode dan membuat sebuah tes gagal, saya bisa melihat nama metode testnya dan mengidentifikasi behavior kode yg diinginkan. Biasanya satu dari tiga hal-hal ini telah terjadi:
- Saya membuat sebuah bug. Salah saya. Solusi: Perbaiki bugnya.
- Tingkah laku yg diinginkan masih relevan tapi telah pindah di tempat lain. Solusi: Pindahkan testnya dan mungkin ubah testnya.
- Tingkah lakunya tidak lagi betul – Guna sistemnya berubah. Solusi: Hapus testnya.
Yang terakhir biasanya terjadi di proyek-proyek agile saat pengertian anda bertambah. Sayangnya, pemula TDD memiliki rasa takut menghapus tes, seakan-akan hal ini mengurangi kualitas kode mereka.
Guna sebuah kata “should” (harus) akan semakin jelas saat dibandingkan dengan alternatif formal seperti akan atau bakal. “Should” membuat anda menantang alasan dari sebuah tes: “Harus? Betulkah?” Ini membuat lebih mudah untuk memutuskan apakah sebuah tes gagal karena bug yang anda buat atau karena asumsi anda sebelumnya tentang perilaku sistem tidak lagi benar sekarang.
”Behaviour” adalah kata yang lebih berguna dari “test”
Sekarang saya telah memiliki sebuat alat – agiledox – untuk membuang semua kata “test” dan sebuah template untuk setiap nama method. Tiba-tiba saya sadar bahwa salah pengertian orang-orang tentang TDD hampir selalu terhadap kata “test”.
Bukannya berarti testing tidak terkandung dalam TDD – method-method yang dihasilkan adalah cara yang efektif untuk memastikan kode anda bekerja. Hanya, jika method-method tersebut tidak lengkap menjelaskan perilaku dari sistem anda, maka mereka hanya membohongi anda ke rasa aman yang salah.
Saya mulai menggunakan kata “behaviour” daripada “test” dalam cara saya menangani TDD dan saya menemukan bukan saja hal ini terasa cocok tapi juga satu kategori pertanyaan-pertanyaan dengan ajaib hilang. Saya sekarang memiliki jawaban-jawaban terhadap pertanyaan-pertanyaan TDD tersebut. Menamakan test anda sekarang menjadi mudah – yaitu sebuah kalimat menjelaskan perilaku berikutnya yang anda inginkan. Seberapa banyak yang perlu dites menjadi mudah – anda hanya bisa mendeskripsikan perilaku yang terbatas dalam sebuah kalimat. Saat sebuah test gagal, cukup kembali ke proses di atas – entah anda membuat sebuah bug, perilakunya pindah, atau testnya tidak lagi relevan.
Saya menemukan bahwa pergeseran dari berpikir dalam tes menjadi berpikir dalam perilaku sangat berarti sehingga saya memanggil TDD sebagai BDD, atau behavior-driven development.
JBehave menekankan perilaku daripada testing
Pada akhir tahun 2003, saya memutuskan saatnya untuk menaruh uang saya – atau paling tidak waktu saya – pada apa yg saya katakan. Saya mulai menulis sebuah pengganti JUnit yg saya sebut JBehave, yang membuang semua referens kepada testing dan menggantinya dengan sebuah kosakata yang dibangun sekitar memeriksa perilaku. Saya melakukan ini untuk melihat bagaimana sebuah framework bisa berkembang bila saya mengikuti dengan ketat terhadap mantra-mantra behaviour-driven saya. Saya juga berpikir bahwa ini akan menjadi sebuah alat pengajar yang berharga untuk memperkenalkan TDD dan BDD tanpa diganggu oleh kosakata berbasis test.
Untuk mendefinisikan sebuah perilaku dari contoh kelas CustomerLookup, saya membuat sebuah kelas perilaku yang saya sebut, CustomerLookupBehaviour. Kelas tersebut akan memiliki method-method yang dimulai dengan kata “should” (harus). Penjalan perilaku ini akan menginstansikan kelas perilaku tersebut dan memanggil tiap method-method perilaku, seperit JUnit lakukan dengan test-testnya. Kelas tersebut akan melaporkan perkembangannya saat proses dan mencetak rangkuman pada akhirnya.
Milestone pertama saya adalah dalam membuat JBehave memeriksa diri sendiri. Saya hanya menambah perilaku yang membuatnya bisa jalan sendiri. Saya bisa memindahkan semua test JUnit ke perilaku JBehave dan mendapatkan umpan balik seperti halnya dengan JUnit.
Menentukan perilaku paling penting berikutnya
Saya lalu menemukan konsep nilai bisnis. Tentu saja, saya selalu menyadari bahwa saya menulis software karena sebuah alasan, tapi saya tidak pernah terlalu berpikir tentang nilai dari kode yg saya tulis. Rekan lainnya, Chris Matts, seorang bisnis analis, membuat saya berpikir tentang nilai bisnis dalam konteks behaviour-driven development.
Karena saya telah mempunyai pikiran untuk membuat JBehave berdiri sendiri, saya menemukan bahwa salah satu cara yg paling berguna untuk tetap fokus adalah dengan bertanya: Hal paling penting berikut apakah yang sistem ini tidak kerjakan?
Pertanyaan ini membuat anda mengidentifikasi nilai dari sebuah fitur yang anda belum implementasikan dan memprioritaskannya. Ini juga membantu anda memformulasi nama method perilakunya: Sistem ini tidak melakukan X (dimana X adalah sebuah perilaku berguna) dan X penting, berarti sistem ini harus melakukan X; jadi method perilaku anda berikutnya adalah:
public void shouldDoX() { }
Sekarang saya punya sebuah jawaban untuk pertanyaan TDD berikutnya, yaitu dimana harus memulai.
Requirement adalah perilaku juga
Pada saat ini, saya telah memiliki framework yang telah membantu saya mengerti – dan terlebih penting, menjelaskan – bagaimana TDD bekerja dan sebuah pendekatan untuk menghindari semua perangkap-perangkap yg saya temui.
Mendekati akhir 2005, sementara saya sedang menjelaskan penemuan baru saya, kosa kata behavior-based, kepada Matt, dia bilang, “Tapi itu hanya seperti analisis.” Ada jeda panjang saat kami memproses hal ini, dan lalu kami putuskan untuk menerapkan semua cara berpikir behavior-driven ini untuk menjelaskan requirement-requirement. Kalau kami bisa mengembangkan sebuah kosa kata konsisten untuk analis, tester, developer, dan bisnis, maka kami bisa saja dalam arah menghilangkan ketidakjelasan dan masalah komunikasi yang terjadi saat orang-orang teknis berbicara ke orang-orang bisnis.
BDD memberikan sebuah “bahasa bersama” untuk analisis
Pada saat ini, Eric Evans telah menerbitkan bukunya yg laris, Domain-Driven Design. Di dalamnya, dia menjelaskan konsep dari memodelkan sebuah sistem dengan menggunakan bahasa bersama yang didasari domain bisnis, sehingga kosa kata bisnis meresap ke dalam kode pemrograman.
Chris dan saya menyadari bahwa kami sedang berusaha mendefinisikan bahasa bersama untuk analisa proses itu sendiri! Kita sudah memiliki titik awal yg baik. Dalam perusahaan kami sudah ada sebuah template untuk story yg seperti ini:
Sebagai [X]
Saya ingin [Y]
supaya [Z]
dimana Y adalah sebuah fitur, Z adalah guna atau nilai fitur tersebut, dan X adalah orang (atau peran) yg akan diuntungkan. Kekuatannya adalah hal ini memaksa anda untuk mengidentifikasi nilai dari menghasilkan sebuah story saat anda mendefinisikannya. Saat dimana tidak ada nilai bisnis yg berarti, biasanya akan seperti “… Saya ingin [sebuah fitur] supaya [saya hanya ingin saja, ok?].” Ini bisa membuat mudah untuk mengeluarkan requirement-requirement yg tidak penting.
Dari titik mula ini, Matts dan saya akan mendapatkan apa yg tiap tester agile sudah ketahui: “Behavior” (perilaku) dari sebuah story sederhananya adalah acceptance criterianya – bila sebuah sistem memenuhi semua acceptance criteria, sistem tersebut berlaku dengan tepat; bila tidak, maka tidak. Jadi kami membuat sebuah template untuk menangkap acceptance criteria dari sebuah story.
Templatenya harus cukup leluasa sehingga tidak terasa buatan atau membatasi analis, tapi cukup terstruktur sehingga kita bisa memecah story tersebut menjadi fragment-fragment dan mengotomatisasinya. Kami memulai mendeskripsikan acceptance criteria dalam bentuk scenario-scenario, dengan mengambil bentuk sebagai berikut:
Dimana (Given) sebuah konteks awal,
Ketika (When) sebuah event terjadi,
Maka (then) pastikan beberapa hasil-hasil.
Sebagai ilustrasi, mari kita menggunakan contoh klasik sebuah mesin ATM. Salah satu contoh story bisa seperti berikut:
+Judul: Pelanggan menarik uang tunai+
Sebagai seorang pelanggan,
saya ingin menarik tunai dari sebuah ATM,
supaya saya tidak perlu menunggu antrian di bank.
Jadi bagaimana kita tahu kalau kita telah menyelesaikan story ini? Ada beberapa skenario-skenario yg perlu dipikirkan: akun tersebut mungkin saja kredit, akun tersebut mungkin “overdrawn” (ditarik terlalu banyak) tapi masih dalam batas, akun tersebut “overdrawn” melebihi batas. Tentu saja, akan ada skenario-skenario lainnya, seperti bila akun tersebut kredit tapi penarikan ini membuatnya melampaui batas, atau mesinnya tidak memiliki uang tunai yg cukup.
Dengan menggunakan template given-when-then, dua skenario pertama kira-kira seperti ini:
+Skenario 1: Akun sedang dalam kredit+
Dimana akun sedang dalam kredit
Dan kartu pelanggan valid
Dan mesinnya memiliki tunai
Ketika pelanggan meminta tunai
Maka pastikan akun tersebut didebit
Dan pastikan uang tunai dikeluarkan
Dan pastikan kartunya dikembalikan
Perhatikan penggunaan “dan” untuk menyambungkan beberapa “dimana” (given) atau beberapa hasil dengan sangat alami.
+Skenario 2: Akun sudah ditarik terlalu banyak melewati batas+
Dimana akun sudah overdrawn
Dan kartu pelanggan valid
Ketika pelanggan meminta tunai
Maka pastikan pesan penolakan ditampilkan
Dan pastikan uang tunai tidak dikeluarkan
Dan pastikan kartunya dikembalikan
Kedua skenario-skenario tersebut dibuat berdasarkan “event” (peristiwa) yg sama dan bahkan memiliki beberapa “givens” dan hasil-hasil yg sama. Kita ingin menggunakan kembali “given”, “event”, dan hasil-hasilnya.
Acceptance criteria harus bisa dieksekusi
Bagian-bagian dari sebuah skenario – yaitu “givens” (dimana), “event” (peristiwa), dan hasilnya – cukup spesifik untuk direpresentasikan langsung dalam baris kode. JBehave mendefinisikan sebuah model object yg membuat kita bisa langsung mempetakan bagian-bagian dari scenario ke kelas-kelas Java.
Anda tuliskan class yang merepresentasikan setiap “given”:
public class AccountIsInCredit implements Given { public void setup(World world) { ... } } public class CardIsValid implements Given { public void setup(World world) { ... } }
dan satu untuk “event”:
public class CustomerRequestsCash implements Event { public void occurIn(World world) { ... } }
dan seterusnya untuk hasilnya. JBehave lalu menyambungkan semuanya dan mengeksekusinya. JBehave membuat sebuah “dunia”, untuk menyimpan object-object anda, dan memberikannya ke setiap “given” agar mereka bisa mengisi dunia tadi dengan state yg diketahui. JBehave lalu membuat “event”nya terjadi di dunia tersebut, sehingga membuat “behavior” dari sebuah scenario. Terakhir, JBehave melanjutkan hasil-hasil yg kita telah definisikan untuk story tersebut.
Dengan memiliki sebuah kelas untuk merepresentasikan setiap fragment membuat kita bisa menggunakan kembali fragment-fragment ini dalam scenario lain atau story lain. Pada awalnya, fragmen-fragmen ini diimplementasikan dengan “mocks” untuk menset sebuah akun kredit atau sebuah kartu yg valid. Hal ini membuat sebuah titik mula untuk mengimplementasikan “behavior”. Saat anda mengimplementasikan aplikasinya, “given-given”nya dan hasil-hasilnya diubah ke class-class sebenarnya yang anda sudah implementasikan, sehingga pada saat sebuah skenario sudah selesai, mereka menjadi functional test end-to-end yg tepat.
BDD sekarang dan masa depannya
Setelah terhenti sebentar, JBehave kembali dalam pengembangan aktif. Program intinya sudah cukup komplit dan stabil. Langkah berikutnya adalah mengintegrasikan dengan IDE Java populer seperti IntelliJ IDEA dan Eclipse.
Dave Astels telah aktif mempromosikan BDD. Blog miliknya dan artikel-artikel lainnya yg telah dipublikasikan telah memulai sejumlah aktifitas, terutama project rspec yg untuk membuat framework BDD dengan bahasa pemrograman Ruby. Saya telah memulai rbehave, yang akan menjadi implementasi JBehave di Ruby.
Sejumlah teman-teman sekerja saya telah menggunakan teknik-teknik BDD pada berbagai macam proyek-proyek betulan dan telah mendapatkan teknik-teknik ini sangat berhasil. JBehave story runner – bagian yg meverifikasi acceptance criteria – sedang dikembangkan secara aktif.
Visinya adalah untuk memiliki sebuah editor yg lengkap supaya para bisnis analis dan para tester bisa menangkap story-story dalam teks editor biasa yang bisa menghasilkan stub-stub untuk kelas-kelas behavior, semua dalam bahasa domain bisnis. BDD telah berevolusi dengan bantuan dari banyak orang-orang, dan saya sangat berterima kasih kepada mereka semua.