この記事はAteam Hikkoshi Samurai Inc. & Ateam Connect Inc. Advent Calendar 2019 20日目の記事です。
はじめに
ここ一年ほど、社内で自動テスト推進係をしております@ysysysysがお送りいたします。
社内の各プロジェクト、特にRailsプロジェクトでRspecを書きましょうと皆さんとともに日々頑張っております。
今回はRspecを書いていく中で個人的につまづきポイントだったなと思うところをいくつかピックアップしてその対応をご紹介しようかと思います。
その1 let と let!
Rspecではletという機能があります。Spec内ではインスタンス変数で定義するよりもlet(:hoge)と定義するのが一般的なようです。
letをつかえば、その内容はメモリーされ、exampleのたびに再生成するコストが減るとのこと。
ここでつまづいたのが、このletが遅延評価するという特性による挙動です。
FactoryBot.define do
factory :user do
first_name { '太郎' }
last_name { '山田' }
email {'testuser@example.com'}
end
end
このようにFactoryBotでUserを作り、そのModelのテストを行うとします。
describe '#full_name' do
let(:user) { create(:user) }
it '山田太郎を返す' do
expect(user.full_name).to eq '山田太郎'
end
end
ところがこれでは正常に動作しないです。
letが評価されるタイミングはuserを呼び出した時です。なのでfull_name実行時にはuserは作られておらずうまくいかないのです。
describe '#full_name' do
let!(:user) { create(:user) }
it '山田太郎を返す' do
expect(user.full_name).to eq '山田太郎'
end
end
letの後ろに!をつけることで、itの中身が実行される前に中身が作られるようにするとうまくいきます。
その2 サブドメインのテスト
こんな感じのルートがあった時に、サブドメイン配下のRequest specを書きたい場合はどうすればいいかハマりました。
constraints subdomain: 'hoge' do
get '/' => subdomain#index
end
正解はこちら⬇︎
describe 'GET index' do
it "200を返すこと。" do
headers = { "Host" => "hoge.example.com" }
get '/', headers: headers
expect(response).to have_http_status(:ok)
end
end
このようにgetする際のheaderにHostを引っ付けてあげればいけるんですね〜
確認するとちゃんとサブドメインが取れています。
pry(#<RSpec::ExampleGroups::SubdomainController>) request.subdomain
=> "hoge"
その3 モバイルのテストをしたい!
テンプレートをPCかスマホで出し分けている時、スマホでもテストをしたい場合はどうすればいいかです。
結論から言うとこれもheadersをいじります。
describe 'GET index' do
it "スマホで200を返すこと" do
headers = { "Host" => "HTTP_USER_AGENT" => "Mozilla/5.0 (iPhone; CPU iPhone OS 11_4 like Mac OS X)" }
get '/', headers: headers
expect(response).to have_http_status(:ok)
end
end
これでモバイルだけおかしい!みたいなこともなく安心。
その4 セッションを持たせておきたい
ログイン先のページをテストする際などセッションがあることを前提とした部分をてすとしたいときです。
こうしましょう
describe 'GET mypage' do
setup do
post "/login", params: {email: 'test@example.com', pass: 'testtest' }
end
it "200を返すこと" do
get '/mypage'
expect(response).to have_http_status(:ok)
end
end
setupブロックで事前に/loginにpostしておくことでセッションを生成しておくことができます。
そのあとにexampleを書いていけばOK!
その5 CarrierWaveを使っているところのテスト
Carrierwaveを使っていて画像をアップロードする部分のテストはどうすればいいの!
before(:each) do
allow_any_instance_of(ImageUploader).to receive(:store!).and_return(true)
end
let(:params) do
{
image: {
file: Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec/images/test.jpg')),
}
}
end
it 'Imageがアップロードされている。' do
post '/upload', params: params
expect(Image.count).to eq 1
end
テスト用の画像ファイルをプロジェクト配下のどこかに置いておいて、読み込んであげればOKです。
私の場合は画像をS3にアップしていたのでその処理をスキップするためにUploaderのstore!メソッドをスタブしました。
本当は環境ごとに適切に分けておいた方がいいんでしょうが、、
まとめ
Rspecを書いていく中で、個人的に印象に残っているつまづきポイントをざっと紹介しました。
こんな感じでつまづくところはつまづきつつ、日々テストコード書いてプロダクトの品質向上に努めております。
お知らせ
エイチームグループでは一緒に活躍してくれる優秀な人材を募集中です。
興味のある方はエイチームグループ採用ページよりご応募くださいませ。
Qiita Jobsのエイチーム引越し侍社内システム企画 / 開発チーム、社内システム開発エンジニアを募集!からチャットでご質問いただくことも可能でございます。
よろしくお願いいたします!
明日は
明日は弊社技術開発部の貴公子@nao_70さんの登場です。お楽しみに。