Membedah testing Rails: Minitest bawaan vs RSpec, fixtures dan factory, unit test model & controller, system test dengan Capybara, serta alur TDD yang mengintegrasikan test ke workflow harian

Setelah di episode 9 kalian membangun autentikasi, episode 10 membahas praktik yang membuat semua kode itu bisa diubah tanpa rasa takut: testing. Rails lahir dengan kultur testing — setiap generator otomatis membuat file test — dan episode ini menjelaskan cara memanfaatkannya secara penuh.
Mengapa testing harus dikuasai dalam? Karena aplikasi yang tumbuh tanpa test akan berhenti berkembang: setiap perubahan berisiko merusak fitur lain secara diam-diam. Test adalah jaring pengaman yang memberi kalian kepercayaan untuk refactor, upgrade gem, dan menambah fitur. Di dunia kerja, "apakah ada test-nya?" hampir selalu menjadi pertanyaan pertama saat review.
Rails menggunakan Minitest secara default; RSpec adalah alternatif populer dengan DSL lebih ekspresif. Keduanya menguji hal yang sama.
class PostTest < ActiveSupport::TestCase
test "post dengan title dan body valid" do
post = Post.new(title: "Judul", body: "Isi")
assert post.valid?
end
test "post tanpa title tidak valid" do
post = Post.new(body: "Isi")
assert_not post.valid?
end
endRSpec menawarkan DSL yang lebih ekspresif untuk assertion yang sama (install via bundle add rspec-rails):
RSpec.describe Post, type: :model do
it { is_expected.to validate_presence_of(:title) }
endPilih salah satu dan konsisten. Series ini memakai Minitest karena bawaan Rails; RSpec dibahas sebagai perbandingan karena dominan di industri.
Data test disiapkan dua cara: fixtures (YAML bawaan Rails) dan factories (gem seperti FactoryBot).
one:
user: rina
title: "Post pertama"
body: "Isi post pertama"
published: trueFixtures otomatis di-load sebelum setiap test; direferensikan sebagai posts(:one).
FactoryBot.define do
factory :post do
user
sequence(:title) { |n| "Post #{n}" }
body { "Isi body" }
published { true }
end
endFactory lebih fleksibel untuk membuat data bervariasi dengan cepat. Fixtures unggul untuk data statis yang dibutuhkan semua test.
Model test menguji validasi, associations, dan method bisnis:
class PostTest < ActiveSupport::TestCase
test "title wajib ada" do
post = posts(:one)
post.title = nil
assert_not post.valid?
assert_includes post.errors[:title], "can't be blank"
end
test "scope published hanya menampilkan post terbit" do
published = Post.published
assert published.all?(&:published?)
assert_includes published, posts(:one)
end
endController test memverifikasi status code, redirect, dan perubahan data:
class PostsControllerTest < ActionDispatch::IntegrationTest
setup { @post = posts(:one) }
test "index mengembalikan 200" do
get posts_url
assert_response :success
end
test "create menambah post hanya saat login" do
assert_no_difference("Post.count") do
post posts_url, params: { post: { title: "X", body: "Y" } }
end
sign_in users(:rina)
assert_difference("Post.count", 1) do
post posts_url, params: { post: { title: "X", body: "Y" } }
end
end
endsign_in adalah helper yang memanipulasi session — generator Authentication Rails 8 menyediakannya.
Tip
assert_difference/assert_no_difference adalah cara paling jelas menguji efek samping (apakah baris dibuat, apakah email terkirim). Gunakan keduanya untuk memastikan perubahan data, bukan hanya status code.
System test menjalankan browser sungguhan (via Cuprite/Playwright/Capybara) untuk menguji alur end-to-end:
class PostsTest < ApplicationSystemTestCase
test "user membuat post dari halaman new" do
sign_in users(:rina)
visit new_post_path
fill_in "Title", with: "Post via browser"
click_on "Create Post"
assert_text "Post via browser"
end
endSystem test menguji integration sungguhan: routing, controller, model, view, dan JavaScript Hotwire. Jalankan dengan:
bin/rails test # unit + integration
bin/rails test test/system # system test (browser)TDD (Test-Driven Development) mengubah urutan kerja: tulis test dulu, lihat gagal, baru implementasi. Alur harian yang realistis:
bin/rails test → test gagal (merah).assert_equal Post.count — rawan race; gunakan assert_difference.Episode 10 membekali kalian testing Rails: Minitest default vs RSpec, fixtures dan FactoryBot, unit test model & controller, system test dengan Capybara yang menjalankan browser sungguhan, serta alur TDD yang mengintegrasikan test ke workflow harian.
Inti yang harus dibawa pulang:
Di episode 11 selanjutnya kita akan membedah Active Job & Action Mailer — queue adapters (Solid Queue, Sidekiq), background jobs, mailer dengan template dan delivery options, plus praktik notifikasi email async. Sampai jumpa di episode 11!