LoginSignup
0
1

More than 3 years have passed since last update.

【RSpec】Active Hashを用いたカテゴリーでテストをパスする方法

Posted at

https://teratail.com/questions/275207
結論この記事

今回は、active hashを用いたカテゴリーの際に、テストがパスしずらい問題を解決していきます

詳しくいうと、

コントローラーの単体テストで、

    it 'showアクションにリクエストすると,そのコーヒーの産地が表示' do
      get drink_path(@drink)
      expect(response.body).to include('マルチリージョン')
    end

このようなテストがパスできなかったです。

期待する動作としては、

スクリーンショット 2021-04-15 16.17.54.png

この画像の右部分に,
「産地 マルチリージョン」
とあるので、上記のようなテストを書きましたが、
drinks/show.html.erb

                <% if @drink.region %>
                  <th class="region-title">産地 </th>
                   <td class="region-name"><%= @drink.region.name%> </td>             
                <% end %>

@drinks.region.name には、「マルチリージョン」という文字が入って欲しかったのですが、

上記のテストを実行した結果、

 Drinks GET #show showアクションにリクエストすると,そのコーヒーの産地が表示
     Failure/Error: expect(response.body).to include('マルチリージョン')

       expected "\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Details | Coffee Passport</title>\n    \n    \n\n   ...script src=\"/packs-test/js/footer-c1b52f0e268f9a453b2e.js\"></script>\n   \n  </body>\n</html>\n\n" to include "マルチリージョン"
       Diff:

中略

       +              <tr class="item-region">
       +                  <th class="region-title">産地 </th>
       +                   <td class="region-name"> </td>             
       +              </tr>

このようなHTMLが返され、

<td class="region-name"> </td>

ここに @drink.region.nameが入るはずですが、
何も入ってなかったので、色々試しました。
色々試した結果は、「teratailに質問しようとした文」の部分に色々書いてあります。

該当のコード

drinks/show.html.erb

   <% if @drink.region %>
     <th class="region-title">産地 </th>
     <td class="region-name"><%= @drink.region.name%> </td>             
    <% end %>

models/region.rb

class Region < ActiveHash::Base
  self.data = [
    { id: 1, name: '---'},
    { id: 2, name: 'マルチリージョン' },
    { id: 3, name: 'ラテンアメリカ' },
    { id: 4, name: 'アフリカ' },
    { id: 5, name: 'アジア、太平洋' }
  ]
end

factories/drinks.rb

FactoryBot.define do
  factory :drink do
    name                {"TOKYOロースト"}
    price               {350}
    explain             {"これはコーヒーの説明です"}
    region_id           {2}

このようなactive hashを用いた構成の場合に、しっかり<%= @drink.region.name%>
が表示されてるか確かめたいので、

spec/requests/drinks_spec.rb

    it 'showアクションにリクエストすると,そのコーヒーの産地が表示' do
      get drink_path(@drink)
      expect(response.body).to include('マルチリージョン')
    end

と、テストを書いた。

しかし、これだと、先ほど説明した通りに上手くいかない

spec/requests/drinks_spec.rb

require 'rails_helper'
# bundle exec rspec spec/requests/tweets_spec.rb

RSpec.describe "Drinks", type: :request do
  before do
    @user = FactoryBot.create(:user)
    region_id = Region.find_by(name: 'マルチリージョン').id
    body_id = Body.find_by(name: "LIGHT(軽い)").id
    acidity_id = Acidity.find_by(name: "LOW(少ない)").id
    processing_id = Processing.find_by(name: "WASHED(水洗式)").id
    @drink = FactoryBot.create(:drink,
                                name: "TOKYOロースト",
                                price: 350,
                                explain: "これはコーヒーの説明です",
                                region_id: region_id,
                                body_id: body_id,
                                acidity_id: acidity_id,
                                processing_id: processing_id)

結論このように書けば上手くいく

models/region.rb

class Region < ActiveHash::Base
  self.data = [
    { id: 1, name: '---'},
    { id: 2, name: 'マルチリージョン' },
    { id: 3, name: 'ラテンアメリカ' },
    { id: 4, name: 'アフリカ' },
    { id: 5, name: 'アジア、太平洋' }
  ]
end

spec/requests/drinks_spec.rb

    region_id = Region.find_by(name: 'マルチリージョン').id

    @drink = FactoryBot.create(:drink,
                                name: "TOKYOロースト",
                                price: 350,
                                explain: "これはコーヒーの説明です",
                                region_id: region_id,

これで、うまいこと取得できて、テストをパスできる

多分これは冗長で、ベストプラクティスではないような気がするので、プロのRubistの皆さんにご意見をお伺いしたいと思うので、それを追記していきます。

teratailに質問しようとした文

このアプリのgithub

起きてる問題点はこれに近いです

発生している問題・エラーメッセージ

 Drinks GET #show showアクションにリクエストすると,そのコーヒーの産地が表示
     Failure/Error: expect(response.body).to include('マルチリージョン')

       expected "\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Details | Coffee Passport</title>\n    \n    \n\n   ...script src=\"/packs-test/js/footer-c1b52f0e268f9a453b2e.js\"></script>\n   \n  </body>\n</html>\n\n" to include "マルチリージョン"
       Diff:

中略

       +              <tr class="item-region">
       +                  <th class="region-title">産地 </th>
       +                   <td class="region-name"> </td>             
       +              </tr>

 <td class="region-name"> </td>   

ここに、コーヒーの産地が表示されるはずですが、テストで返されたHTMLには表示されてないので、テストがパスできません。

原因は、

スクリーンショット 2021-04-15 16.17.54.png

この画像の、この投稿の産地の部分の「マルチリージョン」とか 、コクの「LIGHT(軽い)」の部分とかが、上手く表示されてないです。

該当のソースコード

drinks/show.html

<% if @drink.region %>
<th class="region-title">産地 </th>
<td class="region-name"><%= @drink.region.name%> </td>
<% end %>

と、こんな感じで、drinks/showでは、
その投稿の産地の名前が表示される仕様になっています

region.rb

class Region < ActiveHash::Base
  self.data = [
    { id: 1, name: '---'},
    { id: 2, name: 'マルチリージョン' },
    { id: 3, name: 'ラテンアメリカ' },
    { id: 4, name: 'アフリカ' },
    { id: 5, name: 'アジア、太平洋' }
  ]

end

ここで、Active Hashを用いてることが原因となっております

drinks_spec.rb

    it 'showアクションにリクエストすると,そのコーヒーの産地が表示' do
      get drink_path(@drink)
      expect(response.body).to include('マルチリージョン')
    end

spec/factories/drinks.rb

FactoryBot.define do
  factory :drink do
    name                {"TOKYOロースト"}
    price               {350}
    explain             {"これはコーヒーの説明です"}
    region_id           {Region.all.sample}
    body_id             {Body.all.sample}
    acidity_id          {Acidity.all.sample}
    processing_id       {Processing.all.sample}
    likes_count         {2}
    association :user 
    association :region
    association :body
    association :acidity
    association :processing

    after(:build) do |drink|
      drink.image.attach(io: File.open('app/assets/images/ethiopia.jpg'), filename: 'ethiopia.jpg')
    end
  end
end

spec/factories/regions.rb

FactoryBot.define do
  factory :region do
  end
end

試したこと

この二つの記事を参考にして、

まず、factorybot同士でアソシエーションを組んでないから、@drink.region.nameが表示されないと思って、

spec/factories/regions.rb

FactoryBot.define do
  factory :region do
    association :drink
  end
end

と記述したら

死ぬほど長いエラーが表示され、SystemStackError stack level too deep
こんな感じのエラーが出ました。

次に

app/models/region.rb

class Region < ActiveHash::Base
  self.data = [
    { id: 1, name: '---'},
    { id: 2, name: 'マルチリージョン' },
    { id: 3, name: 'ラテンアメリカ' },
    { id: 4, name: 'アフリカ' },
    { id: 5, name: 'アジア、太平洋' }
  ]
  + include ActiveHash::Associations
   + has_many :drink
end

このように記述しました
一番最初のエラーと変わりませんでした。

factories/region.rb

FactoryBot.define do
  factory :region do
    2      {'マルチリージョン'}
  end
end

このように記述したら

SyntaxError:
  /coffee_passport/spec/factories/regions.rb:3: syntax error, unexpected '{', expecting end
      2      {'マルチリージョン'}
             ^

このようなエラーになりました。

factores/drinks.rb

    region_id           {Region.second}

このように記述しなおしたら

      Failure/Error: region_id           {Region.second}

      NoMethodError:
        undefined method `second' for Region:Class
        Did you mean?  send

とエラーが表示されました。

0
1
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
0
1