はじめに
非同期でAPIにPOSTを投げて、データベースにレコードが作成されてからテストを始めたいみたいなときに、単純にsleep 1
みたいなことをやってしまうとテスト実行環境のパフォーマンスによって落ちてしまう可能性があり、あまり好ましくないですよね💧
そういうときに役に立ちそうな特定の条件がtrueを返すまでsleepするメソッドを作ったのでメモしておきます✍
実際のコード
下記が実際のコードです、wait_condition
にブロックを渡すのそのblockがtrueを返すまで処理を待機します。一応引数にinteval
を渡すと渡したブロックを実行する間隔を指定できて、limit
を渡すと待機する最大の秒数を定義します。
※limitに指定した秒数を超えても最低1回はブロックが実行されてしまうので、注意してください⚠
def wait_condition(interval: 0.5, limit: 10, &condition) start_at = Time.now raise "must give block!" unless block_given? while !condition.call do sleep interval break puts("time out") if (Time.now - start_at) > limit end end def two_sec_afert_true sleep 2 true end wait_condition { two_sec_afert_true } puts "fire!"
ちなみに上記のコードを実行すると約2秒後に"fire!"
がが出力されます🔥
例: DBにレコードが作成されるまで待つ
内部のAPIの呼び出しを行い、実際にデータが作成されるまで待つようなケースでは下記のようなコードで待つことができます⏳
※Mock作ったほうがいいとかそういうケースもありますが、、、
RSpec.describe Book, type: :model do def wait_condition(interval: 0.5, limit: 10, &condition) start_at = Time.now raise "must give block!" unless block_given? while !condition.call do sleep interval break puts("time out") if (Time.now - start_at) > limit end end before do # Bookを作成するAPIにPOSTを投げる処理を実行 wait_condition { !Book.count.zero? } # Bookが1件でも作成されるまで待機 end it 'Bookが作成されること' do expect(Book.take.title).to eq 'book title' end end
おわりに
sleep 1
とかを使ってしまうとたまに落ちるテストになってしまう可能性があるので、なるべく特定の条件で待つようにするか、Mock等を用意するようにしたいですね👀