Saya menggunakan Ruby on Rails 4 dan permata rspec-rails 2.14. Untuk objek saya, saya ingin membandingkan waktu saat ini dengan updated_at
atribut objek setelah tindakan pengontrol dijalankan, tetapi saya dalam masalah karena spesifikasi tidak lulus. Artinya, yang diberikan berikut ini adalah kode speknya:
it "updates updated_at attribute" do
Timecop.freeze
patch :update
@article.reload
expect(@article.updated_at).to eq(Time.now)
end
Ketika saya menjalankan spesifikasi di atas, saya mendapatkan kesalahan berikut:
Failure/Error: expect(@article.updated_at).to eq(Time.now)
expected: 2013-12-05 14:42:20 UTC
got: Thu, 05 Dec 2013 08:42:20 CST -06:00
(compared using ==)
Bagaimana saya bisa membuat spesifikasi lulus?
Catatan : Saya juga mencoba yang berikut (perhatikan utc
tambahannya):
it "updates updated_at attribute" do
Timecop.freeze
patch :update
@article.reload
expect(@article.updated_at.utc).to eq(Time.now)
end
tetapi spesifikasi masih tidak lulus (perhatikan perbedaan nilai "dapatkan"):
Failure/Error: expect(@article.updated_at.utc).to eq(Time.now)
expected: 2013-12-05 14:42:20 UTC
got: 2013-12-05 14:42:20 UTC
(compared using ==)
ruby-on-rails
ruby
time
rspec
comparison
Backo
sumber
sumber
===
, tapi itu mungkin menderita karena melewati batas kedua. Mungkin yang terbaik adalah menemukan atau menulis matcher Anda sendiri, di mana Anda mengubahnya menjadi epoch detik dan membiarkan perbedaan absolut kecil.===
alih-alih==
- saat ini Anda membandingkan object_id dari dua objek Waktu yang berbeda. Meskipun Timecop tidak akan membekukan waktu server database. . . jadi jika cap waktu Anda dibuat oleh RDBMS, itu tidak akan berfungsi (saya harap itu tidak menjadi masalah bagi Anda di sini)Jawaban:
Objek Ruby Time mempertahankan presisi yang lebih baik daripada database. Saat nilai dibaca kembali dari database, itu hanya dipertahankan ke presisi mikrodetik, sedangkan representasi dalam memori tepat ke nanodetik.
Jika Anda tidak peduli dengan perbedaan milidetik, Anda dapat melakukan to_s / to_i di kedua sisi ekspektasi Anda
atau
Lihat ini untuk informasi lebih lanjut tentang mengapa waktunya berbeda
sumber
Timecop
permata. Haruskah itu menyelesaikan masalah dengan "membekukan" waktu?be_within
adalah yang benarexpect {FooJob.perform_now}.to have_enqueued_job(FooJob).at(some_time)
Saya tidak yakin.at
matcher akan menerima waktu yang dikonversi ke integer dengan.to_i
siapa pun yang menghadapi masalah ini?Saya merasa menggunakan
be_within
matcher rspec default lebih elegan:sumber
to_s
. Juga,to_i
danto_s
mungkin jarang gagal jika waktunya mendekati akhir dari satu detik.be_within
matcher telah ditambahkan ke RSpec 2.1.0 pada 7 November 2010, dan ditingkatkan beberapa kali sejak saat itu. rspec.info/documentation/2.14/rspec-expectations/…undefined method 'second' for 1:Fixnum
. Apakah ada yang perlu saya lakukanrequire
?.second
adalah ekstensi rel: api.rubyonrails.org/classes/Numeric.htmlya seperti
Oin
yang disarankanbe_within
matcher adalah praktik terbaik... dan memiliki beberapa uscases lagi -> http://www.eq8.eu/blogs/27-rspec-be_within-matcher
Tetapi satu cara lagi untuk mengatasinya adalah dengan menggunakan Rails bawaan
midday
danmiddnight
atribut.Sekarang ini hanya untuk demonstrasi!
Saya tidak akan menggunakan ini dalam pengontrol karena Anda menghentikan semua panggilan Time.new => semua atribut waktu akan memiliki waktu yang sama => mungkin tidak membuktikan konsep yang Anda coba capai. Saya biasanya menggunakannya di Objek Ruby yang dibuat seperti ini:
Tapi sejujurnya saya merekomendasikan untuk tetap menggunakan
be_within
matcher bahkan saat membandingkan Time.now.midday!Jadi ya tolong tetap dengan
be_within
matcher;)perbarui 2017-02
Pertanyaan dalam komentar:
Anda dapat meneruskan matcher RSpec ke
match
matcher (jadi misalnya Anda bahkan dapat melakukan pengujian API dengan RSpec murni )Adapun "post-db-times" yang saya maksud adalah string yang dihasilkan setelah disimpan ke DB. Saya akan menyarankan untuk memisahkan kasus ini menjadi 2 ekspektasi (satu memastikan struktur hash, kedua memeriksa waktu) Jadi Anda dapat melakukan sesuatu seperti:
Tetapi jika kasus ini terlalu sering dalam rangkaian pengujian Anda, saya sarankan untuk menulis pencocokan RSpec Anda sendiri (misalnya
be_near_time_now_db_string
) mengonversi waktu string db ke objek Waktu dan kemudian menggunakan ini sebagai bagian darimatch(hash)
:sumber
expect(hash_1).to eq(hash_2)
pekerjaan ketika beberapa nilai hash_1 adalah waktu pra-db dan nilai yang sesuai dalam hash_2 adalah waktu pasca-db?Posting lama, tapi saya harap ini membantu siapa saja yang masuk ke sini untuk mendapatkan solusi. Saya pikir lebih mudah dan lebih dapat diandalkan untuk hanya membuat tanggal secara manual:
Ini memastikan tanggal yang disimpan adalah yang benar, tanpa melakukan
to_x
atau mengkhawatirkan desimal.sumber
Anda dapat mengonversi objek tanggal / waktu / waktu menjadi string seperti yang disimpan dalam database dengan
to_s(:db)
.sumber
Cara termudah yang saya temukan untuk mengatasi masalah ini adalah dengan membuat
current_time
metode pembantu tes seperti:Sekarang waktu selalu dibulatkan ke milidetik terdekat untuk perbandingan langsung:
sumber
.change(usec: 0)
sangat membantu.change(usec: 0)
trik ini untuk menyelesaikan masalah tanpa menggunakan spec helper juga. Jika baris pertama adalahTimecop.freeze(Time.current.change(usec: 0))
maka kita dapat membandingkan.to eq(Time.now)
di bagian akhir.Karena saya membandingkan hash, sebagian besar solusi ini tidak berfungsi untuk saya, jadi saya menemukan solusi termudah adalah dengan mengambil data dari hash yang saya bandingkan. Karena waktu updated_at sebenarnya tidak berguna bagi saya untuk menguji ini berfungsi dengan baik.
sumber