この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
自分がRubyでENVを使ったコードを書くときに気をつけていることは、「`ENV.[]` より `ENV.fetch` を使う」と「環境変数の値に関わるテストは`[]`メソッドをstubする」です。 以下でその理由について解説します。
ENV.[] ではなく ENV.fetch を使う
# こうではなく
ENV['hoge']
# こう書く
ENV.fetch('hoge')
理由は以下のとおり
ENV.[]
場合、環境変数が未定義だと nil になる- 環境変数の参照している箇所ではなく、その取得した値を利用する箇所で想定外の挙動となり原因を見つけるのに時間が取られる。
ENV['hoge']' || 'default'
のようにnilとなるのを防ぐことはできるが、忘れる場合があり確実じゃない。
ENV.fetch
の場合、環境変数が未定義だと例外が発生するENV.fetch
の箇所で例外が発生するので環境の不備、あるいはコードの不備であることがすぐにわかるENV.fetch('hoge', 'default')
のように第2引数を使えば環境変数が未定義の場合のデフォルト値が指定できる。
使い捨てのコードや自分しか触らないプライベートなコードであれば、ENV.[]
でも良いですが、プロダクションなコードではfetch
を使う方がメリットが大きいと考えます。
Rspec では ENV[] で直接値を書き換えず必ず stub する
# こうではなく
it "xxx" do
# spec 実行前の値をバックアップして書き換え
@env_backup = ENV['hoge']
ENV['hoge'] = 'fuga'
end
after do
# spec 実行後にもとの値に戻す
ENV['hoge'] = @env_backup
end
# こう書く
before do
allow(ENV).to receive(:[]).and_call_original
allow(ENV).to receive(:[]).with('hoge').and_return('fuga')
end
理由は以下のとおり
- 前者の場合、環境変数のスコープはグローバルなため影響がグローバルに波及する
after do ... end
で元に戻すのを忘れた場合、タイミングによって spec が失敗する状況になり、その調査に時間が取られる。- 実際には試したことないが、specを並列に実行させた場合にも相互に影響が出る?
- 後者の場合、影響範囲は実行中のspecの中だけで、値を戻し忘れるということも発生しない。
参考: RspecでENVをどうstubするのがよいのか – Qiita
まとめ
ENV.[]
よりENV.fetch
を使ってコードが動作するために環境変数の設定が必要であることをコードで明示しよう。- Rspecでは
ENV.[]
メソッドをstubすることで影響範囲を限定しよう