16
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

この記事は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が遅延評価するという特性による挙動です。

user_factory.rb
FactoryBot.define do
  factory :user do
    first_name { '太郎' }
    last_name { '山田' }
    email {'testuser@example.com'}
  end
end

このようにFactoryBotでUserを作り、そのModelのテストを行うとします。

user_spec.rb
describe '#full_name' do
  let(:user) { create(:user) }
  it '山田太郎を返す' do
    expect(user.full_name).to eq '山田太郎'
  end
end

ところがこれでは正常に動作しないです。
letが評価されるタイミングはuserを呼び出した時です。なのでfull_name実行時にはuserは作られておらずうまくいかないのです。

user_spec.rb
describe '#full_name' do
  let!(:user) { create(:user) }
  it '山田太郎を返す' do
    expect(user.full_name).to eq '山田太郎'
  end
end

letの後ろに!をつけることで、itの中身が実行される前に中身が作られるようにするとうまくいきます。

その2 サブドメインのテスト

こんな感じのルートがあった時に、サブドメイン配下のRequest specを書きたい場合はどうすればいいかハマりました。

routes.rb
constraints subdomain: 'hoge' do
  get '/' => subdomain#index
end

正解はこちら⬇︎

subdomain_request_spec.rb
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をいじります。

request_spec.rb
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 セッションを持たせておきたい

ログイン先のページをテストする際などセッションがあることを前提とした部分をてすとしたいときです。
こうしましょう

request_spec.rb
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を使っていて画像をアップロードする部分のテストはどうすればいいの!

upload_request_spec.rb

  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さんの登場です。お楽しみに。

16
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?