RSpec: Apa perbedaan antara let dan a before?

90

Apa perbedaan antara letdan beforeblok di RSpec?

Dan kapan harus menggunakannya?

Apa pendekatan yang baik (membiarkan atau sebelum) dalam contoh di bawah ini?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

Saya mempelajari posting stackoverflow ini

Tetapi apakah baik untuk mendefinisikan biarkan untuk hal-hal asosiasi seperti di atas?

kriysna
sumber
1
Pada dasarnya, 'let' digunakan oleh orang-orang yang tidak menyukai variabel instan. Sebagai catatan tambahan, Anda harus mempertimbangkan untuk menggunakan FactoryGirl atau alat serupa.
apneadiving

Jawaban:

147

Orang-orang tampaknya telah menjelaskan beberapa cara dasar di mana mereka berbeda, tetapi ditinggalkan before(:all)dan tidak menjelaskan dengan tepat mengapa mereka harus digunakan.

Saya yakin bahwa variabel instan tidak memiliki tempat untuk digunakan di sebagian besar spesifikasi, sebagian karena alasan yang disebutkan dalam jawaban ini , jadi saya tidak akan menyebutkannya sebagai opsi di sini.

biarkan blok

Kode dalam satu letblok hanya dieksekusi ketika direferensikan, pemuatan lambat ini berarti bahwa urutan blok-blok ini tidak relevan. Ini memberi Anda sejumlah besar daya untuk mengurangi penyiapan berulang melalui spesifikasi Anda.

Salah satu contoh (sangat kecil dan dibuat-buat) ini adalah:

let(:person)     { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }

context 'with a moustache' do
  let(:has_moustache) { true } 
  its(:awesome?)      { should be_true }
end

context 'without a moustache' do
  let(:has_moustache) { false } 
  its(:awesome?)      { should be_false }
end

Anda dapat melihat has_moustachedefinisi yang berbeda di setiap kasus, tetapi tidak perlu mengulangi subjectdefinisi tersebut. Sesuatu yang penting untuk diperhatikan adalah letblok terakhir yang ditentukan dalam konteks saat ini akan digunakan. Ini bagus untuk menyetel default agar digunakan untuk sebagian besar spesifikasi, yang dapat ditimpa jika perlu.

Misalnya, memeriksa nilai kembalian calculate_awesomejika meneruskan personmodel dengan top_hatset ke true, tetapi tidak ada kumis akan menjadi:

context 'without a moustache but with a top hat' do
  let(:has_moustache) { false } 
  let(:person)        { build(:person, top_hat: true) }
  its(:awesome?)      { should be_true }
end

Hal lain yang perlu diperhatikan tentang blok let, mereka tidak boleh digunakan jika Anda mencari sesuatu yang telah disimpan ke database (yaitu Library.find_awesome_people(search_criteria)) karena mereka tidak akan disimpan ke database kecuali mereka telah direferensikan. let!atau beforebalok adalah apa yang harus digunakan di sini.

Juga, jangan pernah gunakan beforeuntuk memicu eksekusi letblok, inilah yang let!dibuat untuk!

membiarkan! blok

let!blok dieksekusi dalam urutan yang mereka definisikan (seperti blok sebelumnya). Satu perbedaan inti dengan blok before adalah Anda mendapatkan referensi eksplisit ke variabel ini, daripada harus kembali ke variabel instan.

Seperti halnya letblok, jika beberapa let!blok didefinisikan dengan nama yang sama, yang terbaru adalah apa yang akan digunakan dalam eksekusi. Perbedaan intinya adalah let!blok akan dieksekusi beberapa kali jika digunakan seperti ini, sedangkan letblok hanya akan dieksekusi terakhir kali.

sebelum (: setiap) blok

before(:each)adalah default sebelum blokir, dan oleh karena itu dapat direferensikan before {}daripada menentukan secara penuh before(:each) {}setiap saat.

Ini adalah preferensi pribadi saya untuk menggunakan beforeblok dalam beberapa situasi inti. Saya akan menggunakan sebelum blok jika:

  • Saya menggunakan ejekan, stubbing, atau doubles
  • Ada pengaturan ukuran yang wajar (umumnya ini adalah tanda bahwa sifat pabrik Anda belum diatur dengan benar)
  • Ada sejumlah variabel yang tidak perlu saya rujuk secara langsung, tetapi diperlukan untuk penyiapan
  • Saya sedang menulis tes pengontrol fungsional di rel, dan saya ingin menjalankan permintaan khusus untuk diuji (yaitu before { get :index }). Meskipun Anda dapat menggunakan subjectini dalam banyak kasus, terkadang terasa lebih eksplisit jika Anda tidak memerlukan referensi.

Jika Anda menemukan diri Anda menulis beforeblok besar untuk spesifikasi Anda, periksa pabrik Anda dan pastikan Anda memahami sepenuhnya sifat dan fleksibilitasnya.

sebelum (: semua) blok

Ini hanya akan dieksekusi sekali, sebelum spesifikasi dalam konteks saat ini (dan turunannya). Ini dapat digunakan untuk keuntungan besar jika ditulis dengan benar, karena ada situasi tertentu yang dapat mengurangi eksekusi dan upaya.

Salah satu contoh (yang hampir tidak memengaruhi waktu eksekusi sama sekali) adalah mengejek variabel ENV untuk pengujian, yang seharusnya hanya perlu Anda lakukan sekali.

Semoga membantu :)

Jay
sumber
Untuk pengguna minitest, saya tetapi saya tidak melihat tag RSpec dari Tanya Jawab ini, before(:all)opsi tidak ada di Minitest. Berikut adalah beberapa solusi di komentar: github.com/seattlerb/minitest/issues/61
MrYoshiji
Artikel yang direferensikan adalah tautan mati: \
Dylan Pierce
Terima kasih @DylanPierce. Saya tidak dapat menemukan salinan artikel itu, jadi saya telah mereferensikan jawaban SO yang membahas ini sebagai gantinya :)
Jay
Terima kasih :) sangat dihargai
Dylan Pierce
1
itstidak lagi dalam rspec-core. Cara yang lebih modern dan idiomatis adalah it { is_expected.to be_awesome }.
Zubin
26

Hampir selalu, saya lebih suka let. Posting yang Anda tautkan letjuga lebih cepat. Namun, terkadang, ketika banyak perintah harus dijalankan, saya dapat menggunakannya before(:each)karena sintaksnya lebih jelas ketika banyak perintah yang terlibat.

Dalam contoh Anda, saya pasti lebih suka menggunakan letdaripada before(:each). Secara umum, ketika hanya beberapa inisialisasi variabel dilakukan, saya cenderung suka menggunakan let.

Spyros
sumber
15

Perbedaan besar yang belum disebutkan adalah bahwa variabel yang ditentukan dengan lettidak dibuat instance-nya sampai Anda memanggilnya pertama kali. Jadi, sementara before(:each)blok akan membuat instance semua variabel, letmari kita tentukan sejumlah variabel yang mungkin Anda gunakan di beberapa pengujian, itu tidak secara otomatis membuat instance-nya. Tanpa mengetahui hal ini, pengujian Anda dapat kembali gagal jika Anda mengharapkan semua data dimuat sebelumnya. Dalam beberapa kasus, Anda bahkan mungkin ingin menentukan sejumlah letvariabel, kemudian menggunakan before(:each)blok untuk memanggil setiap letcontoh hanya untuk memastikan datanya tersedia untuk memulai.

mikeweber
sumber
20
Anda dapat menggunakan let!untuk menentukan metode yang dipanggil sebelum setiap contoh. Lihat dokumen RSpec .
Jim Stewart
3

Sepertinya Anda menggunakan Masinis. Waspadalah, Anda mungkin melihat beberapa masalah dengan make! di dalam let (versi non-bang) terjadi di luar transaksi perlengkapan global (jika Anda menggunakan perlengkapan transaksional juga) sehingga merusak data untuk pengujian Anda yang lain.

Tim Connor
sumber