RSpec adalah framework pengujian yang paling luas digunakan di komunitas Ruby, dan ciri khasnya adalah skenario uji yang ditulis mendekati kalimat deskripsi perilaku. Topik ini membahas struktur berkas spec RSpec dengan pasangan describe dan it, penyusunan expectation beserta matcher-nya, penyiapan keadaan awal lewat hook before, serta cara menjalankan uji dari baris perintah. Setelah membacanya, perilaku sebuah kelas dapat diuji dengan gaya yang menjadi standar banyak proyek Ruby.
Kelas yang diuji dan berkas spec pertama#
Kelas yang digunakan sepanjang topik ini adalah Antrean, yaitu struktur antrean sederhana yang menyimpan nama-nama anggota dan mengeluarkannya dari yang paling depan.
class Antrean
def initialize
@anggota = []
end
def tambah(nama)
@anggota.push(nama)
end
def geser
@anggota.shift
end
def kosong?
@anggota.empty?
end
def ukuran
@anggota.size
end
endBerkas spec ditulis dengan akhiran _spec.rb, dan berikut adalah spec lengkap untuk Antrean. Baris require_relative "antrean" tetap diperlukan untuk memuat kelasnya, tetapi require "rspec" tidak, karena framework dimuat oleh perintah rspec sendiri sebelum berkas spec dibaca.
# tidak-dijalankan: keluaran penuh uji ditampilkan pada blok bash di bawah dengan seed tetap
require_relative "antrean"
RSpec.describe Antrean do
before do
@antrean = Antrean.new
end
describe "#kosong?" do
it "menyatakan true ketika belum ada anggota" do
expect(@antrean).to be_kosong
end
end
describe "#tambah" do
it "menambahkan satu anggota ke antrean" do
@antrean.tambah("Ali")
expect(@antrean.ukuran).to eq(1)
end
end
describe "#geser" do
it "mengembalikan anggota yang paling depan" do
@antrean.tambah("Ali")
@antrean.tambah("Budi")
expect(@antrean.geser).to eq("Ali")
end
it "mengembalikan nil ketika antrean kosong" do
expect(@antrean.geser).to be_nil
end
end
endBerkas spec dijalankan lewat perintah rspec dengan menyebut nama berkasnya. Opsi --no-color mematikan pewarnaan keluaran, sedangkan --order defined menjalankan example sesuai urutan penulisannya, bukan urutan acak.
# menjalankan seluruh example pada antrean_spec.rb
rspec antrean_spec.rb --no-color --order defined 2>&1 | sed '/^Finished in/d'....
4 examples, 0 failuresEmpat titik pada keluaran menyatakan empat example yang lulus, dan baris terakhir merangkum jumlahnya. Struktur berkas spec terbaca dari atas ke bawah sebagai kalimat bertingkat: RSpec.describe Antrean menyatakan kelompok uji untuk kelas Antrean, sedangkan describe bersarang seperti describe "#geser" membagi kelompok itu per metode. Konvensi penamaannya tetap, yaitu tanda # diikuti nama metode untuk menyebut metode instans. Setiap blok it memuat satu example, yaitu satu skenario uji dengan string deskripsi yang dinyatakan sebagai perilaku, misalnya “mengembalikan anggota yang paling depan”.
Expectation dan matcher#
Inti dari setiap example adalah expectation, yaitu pernyataan tentang nilai yang diharapkan, ditulis dengan bentuk expect(nilai).to matcher. Metode expect menerima objek yang diperiksa, lalu metode to menerima matcher yang mendefinisikan kriteria kecocokan. Apabila kecocokan tidak terpenuhi, example dinyatakan gagal dan RSpec mencetak nilai yang diharapkan berdampingan dengan nilai yang diperoleh.
Tiga matcher digunakan pada spec Antrean di atas.
| Matcher | Pemeriksaan yang dilakukan |
|---|---|
eq(1) | kesamaan nilai dengan perbandingan == |
be_nil | kesamaan dengan nil |
be_kosong | pemanggilan metode kosong? pada objek yang diperiksa |
Dua matcher pertama berperilaku persis seperti arti namanya. Matcher be_kosong memperlihatkan keluarga matcher dengan prefiks be_: RSpec menerjemahkan be_kosong menjadi pemanggilan metode kosong? pada @antrean, sehingga predikat kelas biasa dapat digunakan sebagai matcher tanpa kode tambahan. Cara ini berlaku untuk predikat apa pun, misalnya be_kosong untuk kosong? dan be_ukuran_satu untuk ukuran_satu?. Dengan kebiasaan ini, nama metode predikat yang deskriptif di kelas produksi langsung menjadi bahasa pemeriksaan di berkas spec. Tiga matcher pada tabel hanyalah yang digunakan pada spec ini, sedangkan keluarga matcher RSpec jauh lebih luas dan daftar lengkapnya dapat ditelusuri pada dokumentasi resmi RSpec.
Menyiapkan keadaan dengan before#
Blok before adalah hook yang dijalankan sebelum setiap example pada kelompoknya. Pada spec Antrean, blok before membuat objek Antrean baru dan menyimpannya ke variabel instans @antrean, sehingga setiap example memulai dari antrean kosong yang sama. Tanpa hook itu, pembuatan objek harus ditulis berulang di dalam empat example. Variabel instans menjadi penghubung antara hook dan example karena keduanya dievaluasi dalam lingkup objek yang sama. Satu kelompok juga boleh memuat lebih dari satu blok before, dan RSpec menjalankannya berurutan dari atas ke bawah.
Keadaan yang disiapkan before tidak terbawa antar-example. Setiap example memperoleh objek yang dibuat ulang oleh hook, sehingga perubahan yang dilakukan satu example, misalnya menambahkan "Ali" ke antrean, tidak pernah terlihat oleh example berikutnya. Pemisahan keadaan ini membuat setiap example berdiri sendiri, dan urutan eksekusi tidak memengaruhi hasil bahkan setelah example baru ditambahkan ke dalam kelompok.
Kesalahan umum#
Kesalahan yang paling cepat muncul adalah menjalankan berkas spec dengan ruby, seperti kebiasaan pada Minitest. DSL (domain-specific language) milik RSpec, yaitu bahasa khusus untuk menulis uji, tidak dimuat oleh perintah ruby, sehingga baris pertama yang memanggil RSpec.describe langsung berakhir dengan error.
# kesalahan: berkas spec dijalankan dengan ruby, bukan dengan rspec
ruby antrean_spec.rbantrean_spec.rb:4:in '<main>': uninitialized constant RSpec (NameError)
RSpec.describe Antrean do
^^^^^Pesan uninitialized constant RSpec muncul karena DSL hanya dimuat oleh perintah rspec, bukan oleh ruby. Perbaikannya sesederhana mengganti perintah eksekusinya menjadi rspec antrean_spec.rb. Kesalahan yang senada terjadi pada penamaan berkas: secara default, perintah rspec tanpa argumen hanya mencari berkas yang berakhiran _spec.rb, sehingga berkas uji yang dinamai antrean_uji.rb tidak pernah ditemukan meskipun isinya benar.
Kesalahan ketiga yang lazim adalah lupa memuat kelas produksi. Apabila baris require_relative "antrean" dihapus, kelompok RSpec.describe Antrean gagal dimuat dengan NameError: uninitialized constant Antrean sebelum satu example pun berjalan. Keluaran seperti itu selalu berarti masalah pemuatan berkas, bukan masalah perilaku yang diuji.
Ringkasan#
- Berkas RSpec berakhiran
_spec.rb, tidak memerlukanrequire "rspec", dan dijalankan dengan perintahrspec nama_berkas_spec.rb. - Struktur ujinya bertingkat:
describemembentuk kelompok per kelas atau metode, sedangkan setiapitmemuat satu example dengan deskripsi perilaku. - Expectation ditulis
expect(nilai).to matcher, denganeq,be_nil, dan keluargabe_sebagai matcher yang paling sering digunakan. - Blok
beforemenyiapkan keadaan awal untuk setiap example, sehingga antar-example tidak saling memengaruhi.
Referensi#
- RSpec — rspec.info — Situs resmi RSpec yang memuat pengantar gaya describe/it dan tautan dokumentasinya.
- rspec-core 3.13 — RSpec — Dokumentasi resmi rspec-core untuk describe, it, before, serta opsi baris perintah.