Pertama Kali Mencoba RSpec
Prerequisite
ruby 2.6.3
rails 6.0.1
postgresql 11.5
rspec 4.0.0.beta3
Prakata
Sekitar seminggu yang lalu, di saat badan sedang meriang dan meler hebat namun kadang tersumbat, saya mencoba untuk mempelajari salah satu test atau pengujian untuk membantu kita menguji web aplikasi yang kita bangun.
RSpec, adalah salah satu test tersebut.
Selain RSpec, Ruby on Rails sendiri, sudah memiliki built-in test.
Keren bukan!
Test tersebut bernama MiniTest.
MiniTest ini sudah menjadi test bawaan dari Rails sejak versi 5.1.
Ini adalah bukti yang sangat bagus sekali, untuk menjelaskan bahwa dalam membuat sebuah web aplikasi, kita sangat perlu untuk melakukan testing.
Kalau saya tidak ngawur, konsep ngoding sambil melakukan testing itu dikenal dengan Test Driven Development (TDD).
Kenapa RSpec?
Kalau Ruby on Rails sudah membawa MiniTest secara default, lantas mengapa memilih menggunakan RSpec?
Ini pendapat pribadi saya.
Menurut saya, dalam menulis spesifikasi-spesifikasi test, RSpec memiliki sintaks yang mudah untuk dibaca. Karena mudah dibaca tentunya akan mudah untuk dipahami. Bahkan untuk orang non-technical akan sangat mudah memahami spesifikasi test yang ditulis menggunakan RSpec.
Coba perhatikan contoh dibawah ini.
Berikut ini adalah beberapa spesifikasi list pada model Author.
1RSpec.describe Author, type: :model do2 context 'Validation Presence Tests' do3 it 'Ensures full name presence'4 it 'Ensures email presence'5 it 'Ensures password presence'6 it 'Should save successfully'7 end89 context 'Validation Length Tests' do10 it 'Ensures full name character length, more than 5'11 it 'Ensures full name character length, less than 30'12 it 'Ensures email character length, less than 50'13 it 'Ensures password character length, same or more than 8'14 end1516 context 'Email Format Tests' do17 it 'Ensures email format not valid'18 it 'Ensures email format valid'19 end2021 context 'Email Uniqueness Tests' do22 it 'Ensures email has uniqueness'23 end24end
Setiap list tersebut, nantinya akan saya breakdown sesuai dengan konteksnya dan judulnya.
Gimana?
Cukup dapat dimengerti kan, maksud dari list tersebut.
Nah, sekarang saya lanjutkan untuk proses memasang RSpec pada project Rails kita.
Instalasi
Pada project baru, saya menjalankan perintah ini.
$ rails new blog_rspec_test -d postgresql -T
Penambahan option -T
, adalah untuk men-disable built-in test pada project yang baru kita buat.
Tujuannya tentu saja, karena saya akan menggunakan RSpec untuk melakukan testing, bukan menggunakan MiniTest.
Pada project yang sudah ada, langsung saja mengikuti langkah selanjutnya.
Kemudian, pasang gem rspec-rails pada block :development
dan :text
di Gemfile
project.
1# ...2# ...34group :development, :test do5 # ...6 gem 'rspec-rails', '~> 4.0.0.beta3'7end
Tujuan memasukkan gem ini pada group :development
agar lebih mudah. Karen, kalau hanya pada group :test
, kita hanya dapat mendapatkan gem ini evinmonment test (RAILS_ENV=test
).
Saya menggunakan versi 4.0.0.beta3 karena terdapat test yang sudah deprecated pada versi sebelumnya, yaitu pada controller test. Karena belum begitu memahami lebih jauh tentang RSpec, saya mengikuti saja saran dari teman-teman yang sudah lebih dulumencoba versi beta ini.
Selanjutnya, seperti biasa, setiap setelah menambahkan gem baru pada Gemfile
, kita perlu menjalankan perintah,
$ bundle install
Setelah proses instalasi selesai, kita juga perlu meng-generate boilerplate dari konfigurasi yang sudah disediakan oleh rspec-rails.
$ rails generate rspec:install
Hasil generate tersebut, akan membuat beberapa file konfigurasi pada direktori rspec/
.
Running via Spring preloader in process 28211
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
Langkah selanjutnya ini bersifat optional, tapi saya memilih untuk melakukannya.
Tambahkan --format documentation
, pada file .rspec
.
--require spec_helper
--format documentation
Penambahan ini bertujuan untuk mengganti format default output yang ditampilkan oleh RSpec menjadi lebih mudah untuk dibaca.
Kalau tidak ingin menambahkannya sekarang, kita juga dapat menambahkannya dilain waktu.
Nah, sekarang kita dapat lanjut pada tahapan membuat spesifikasi test.
Model Specs
Saya sependapat dengan pernyataan bahwa, “Untuk memahami apa itu test, paling mudah kita mulai dari model test.”
Nah, karena alasan itu saya memulai dari spesifikasi model terlebih dahulu.
Kenapa, karena kita dapat memanfaatkan validation yang terdapat di dalam model. Hihihi.
RSpec juga sudah menyediakan spec file generator. Tinggal kita pergunakan saja. Enak sekali kan.
$ rails generate rspec:model nama_model
Pada, kasus ini, saya memiliki nama model author
.
$ rails generate rspec:model author
Maka, rspec akan men-generate satu file spec untuk kita.
create spec/models/author_spec.rb
Untuk melihat daftar dari generator apa saja yang disediakan oleh RSpec, dapat menggunakan perintah,
$ rails generate --help | grep rspec
Sebelum saya menjabarkan spesifikasi model test untuk model author, saya akan menunjukkan isi dari model author yang di dalamnya terdapat daftar validation dari model author.
1class Author < ApplicationRecord2 has_many :articles34 # Validations5 validates :full_name, presence: true,6 length: {minimum: 5, maximum: 30}78 VALID_EMAIL_REGEX = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i9 validates :email, presence: true,10 uniqueness: {case_sensitive: false},11 length: {maximum: 50},12 format: {with: VALID_EMAIL_REGEX }1314 validates :password, presence: true,15 length: {minimum: 8}16end
Nah, sekarang pasti sudah mengerti kan, kenapa untuk belajar, sangat mudah kita mulai dari model test.
Karena kita akan menguji fungsi dari validation yang sudah kita definisikan pada model author.
Oke sekarang langsung saja, saya akan menjabarkan spesifikasi untuk menguji validation untuk presence: true
pada setiap field.
Kita dapat melihat pada validation model author tersebut, setiap field memiliki presence validation.
Saya akan mulai dari filed full_name
terlebih dahulu.
1require 'rails_helper'23RSpec.describe Author, type: :model do4 context 'Validation Presence Tests' do5 it 'Ensures full name presence' do6 author = Author.new(7 full_name: nil,8 email: Faker::Internet.free_email,9 password: Faker::Team.name.downcase.strip.gsub(' ', '')10 ).save11 expect(author).to eq(false)12 end13 end14end
Pada spesifikasi ini, saya hanya membuat filed full_name
bernilai nil
. Dengan maksud membuat field tersebut kosong. Dan test dianggap benar, apabila hasil dari spesifikasi tersebut bernilai salah ` eq(false)`.
Mudah kan!
Nah, coba perhatikan, terdapat blok yang bernama context
. Ini dapat kita gunakan untuk membuat konteks pada setiap spesifikasi testing yang akan kita lakukan.
Langkah selanjutnya adalah presence validation untuk field email
, maka saya masukkan di dalam context yang sama, dan berada di di bawah spesifikasi untuk field full_name
.
12...3 ...45 it 'Ensures email presence' do6 author = Author.new(7 full_name: Faker::Team.name.titlecase,8 email: nil,9 password: Faker::Team.name.downcase.strip.gsub(' ', '')10 ).save11 expect(author).to eq(false)12 end1314 ...15...
Begitu seterusnya untuk presence validation pada field password
.
Coba teman-teman buat spesifikasinya untuk presence validation pada field password
ini.
Oh ya!
Saya menggunakan bantuan gem faker
untuk meng-generate data dummy. Agar lebih mudah, dan tidak perlu menghabiskan waktu untuk memikirkan data secara manual.
Nah, dengan begitu, saya langsung dapat menuliskan spesifikasi untuk menguji semua spesifikasi validation yang ada pada model author.
Kira-kira seperti ini.
1require 'rails_helper'23RSpec.describe Author, type: :model do4 context 'Validation Presence Tests' do5 it 'Ensures full name presence' do6 author = Author.new(7 full_name: nil,8 email: Faker::Internet.free_email,9 password: Faker::Team.name.downcase.strip.gsub(' ', '')10 ).save11 expect(author).to eq(false)12 end1314 it 'Ensures email presence' do15 author = Author.new(16 full_name: Faker::Team.name.titlecase,17 email: nil,18 password: Faker::Team.name.downcase.strip.gsub(' ', '')19 ).save20 expect(author).to eq(false)21 end2223 it 'Ensures password presence' do24 author = Author.new(25 full_name: Faker::Team.name.titlecase,26 email: Faker::Internet.free_email,27 password: nil28 ).save29 expect(author).to eq(false)30 end3132 it 'Should save successfully' do33 author = Author.new(34 full_name: Faker::Team.name.titlecase,35 email: Faker::Internet.free_email,36 password: Faker::Team.name.downcase.strip.gsub(' ', '')37 ).save38 expect(author).to eq(true)39 end40 end4142 context 'Validation Length Tests' do43 it 'Ensures full name character length, more than 5' do44 author = Author.new(45 full_name: 'ban',46 email: Faker::Internet.free_email,47 password: Faker::Team.name.downcase.strip.gsub(' ', '')48 ).save49 expect(author).to eq(false)50 end5152 it 'Ensures full name character length, less than 30' do53 author = Author.new(54 full_name: 'bandithijobandithijobandithijobandithijo',55 email: Faker::Internet.free_email,56 password: Faker::Team.name.downcase.strip.gsub(' ', '')57 ).save58 expect(author).to eq(false)59 end6061 it 'Ensures email character length, less than 50' do62 author = Author.new(63 full_name: Faker::Team.name.titlecase,64 email: 'bandithijobandithijobandithijobandithijobandithijo@gmail.com',65 password: Faker::Team.name.downcase.strip.gsub(' ', '')66 ).save67 expect(author).to eq(false)68 end6970 it 'Ensures password character length, same or more than 8' do71 author = Author.new(72 full_name: Faker::Team.name.titlecase,73 email: Faker::Internet.free_email,74 password: 'bandit'75 ).save76 expect(author).to eq(false)77 end78 end7980 context 'Email Format Tests' do81 it 'Ensures email format not valid' do82 author = Author.new(83 full_name: Faker::Team.name.titlecase,84 email: 'bandithijo@bandithijo',85 password: Faker::Team.name.downcase.strip.gsub(' ', '')86 ).save87 expect(author).to eq(false)88 end8990 it 'Ensures email format valid' do91 author = Author.new(92 full_name: Faker::Team.name.titlecase,93 email: Faker::Internet.free_email,94 password: Faker::Team.name.downcase.strip.gsub(' ', '')95 ).save96 expect(author).to eq(true)97 end98 end99100 context 'Email Uniqueness Tests' do101 before do102 Author.new(103 full_name: Faker::Team.name.titlecase,104 email: 'bandithijo@gmail.com',105 password: Faker::Team.name.downcase.strip.gsub(' ', '')106 ).save107 end108109 it 'Ensures email has uniqueness' do110 author = Author.new(111 full_name: Faker::Team.name.titlecase,112 email: 'bandithijo@gmail.com',113 password: Faker::Team.name.downcase.strip.gsub(' ', '')114 ).save115 expect(author).to eq(false)116 end117 end118end
Nah, gimana?
Mudah dipahami kan?
Nah, untuk menalankan test-nya gunakan perintah ini,
$ bundle exec rspec
Nanti akan keluar output seperti ini.
Author
Validation Presence Tests
Ensures full name presence
Ensures email presence
Ensures password presence
Should save successfully
Validation Length Tests
Ensures full name character length, more than 5
Ensures full name character length, less than 30
Ensures email character length, less than 50
Ensures password character length, same or more than 8
Email Format Tests
Ensures email format not valid
Ensures email format valid
Email Uniqueness Tests
Ensures email has uniqueness
Finished in 2.1 seconds (files took 8.29 seconds to load)
11 examples, 0 failures
Sesuai dengan jumlah spesifikasi yang kita tulis, ada 11 buah. Dan kesemuanya berhasil.
Selanjutnya untuk controller spec.
Controller Spec
Kita gunakan lagi spec file generator yang sudah disediakan oleh RSpec.
$ rails generate rspec:controller authors
Karena kita akan menguji controller, tentu saja kita mengikuti naming convention dari Rails, yang mengharuskan menggunakan penamaan plural pada controller. Berbeda dengan model yang menggunakan penamaan singular.
Kalau berhasil, maka akan dibuatkan file specnya seperti ini.
create spec/controllers/authors_controller_spec.rb
Belum banyak yang saya pahami mengenai controller spec ini, jadi langsung saja saya tulisakan sedikit contohnya mengenai pengujian untuk response dan route.
Kira-kira seperti ini.
1require 'rails_helper'23RSpec.describe AuthorsController, type: :controller do4 context 'Get #index' do5 it 'Returns a success response' do6 get :index7 expect(response).to be_ok8 end9 end1011 context 'Get #show' do12 it 'Returns a success response' do13 author = Author.create!(14 full_name: Faker::Team.name.titlecase,15 email: Faker::Internet.free_email,16 password: Faker::Team.name.downcase.strip.gsub(' ', '')17 )18 get :show, params: { id: author.to_param }19 expect(response).to be_ok20 end21 end2223 context 'Proper Routes Tests' do24 it 'Should has proper index route' do25 expect(get: '/authors').to be_routable26 end2728 it 'Should has proper show route' do29 expect(get: '/authors/1').to be_routable30 end3132 it 'Should has proper new route' do33 expect(get: '/authors/new').to be_routable34 end3536 it 'Should has proper edit route' do37 expect(get: '/authors/1/edit').to be_routable38 end3940 it 'Should has proper destroy route' do41 expect(delete: '/authors/1').to be_routable42 end43 end44end
Wkwkwkwk.
Pengen ketawa, karena seadanya banget.
Tapi tidak mengapa, saya tetap harus menuliskan catatan mengenai proses belajar ini.
Sebagai jejak belajar yang menunjukkan bahwa saya dulu juga berawal dari belum bisa.
Okeh!
Sepertinya hanya ini saja yang ingin saya tuliskan.
Untuk referensi yang lebih lengkap seputar RSpec pada Rails, dapat dimulai dari membaca dokumentasi pada gem rspec-rails
yang saya sertakan pada referensi di bawah.1
Untuk referensi seputar RSpec dapat membaca pada dokumentasi RSpec yang disediakan di Relishapp.3
Mudah-mudahan dapat sedikit banyak bermanfaat buat teman-teman yaa.
Terima kasih.
(^_^)v
Referensi
-
github.com/rspec/rspec-rails
Diakses tanggal: 2019/12/05 -
Everyday Rails Testing with RSpec : A practical approach to test-driven development by Aaron Sumner
Diakses tanggal: 2019/12/05 -
relishapp.com/rspec/rspec-rails/docs
Diakses tanggal: 2019/12/05