https://teratail.com/questions/275207
結論この記事
今回は、active hashを用いたカテゴリーの際に、テストがパスしずらい問題を解決していきます
詳しくいうと、
コントローラーの単体テストで、
it 'showアクションにリクエストすると,そのコーヒーの産地が表示' do
get drink_path(@drink)
expect(response.body).to include('マルチリージョン')
end
このようなテストがパスできなかったです。
期待する動作としては、
この画像の右部分に,
「産地 マルチリージョン」
とあるので、上記のようなテストを書きましたが、
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には表示されてないので、テストがパスできません。
原因は、
この画像の、この投稿の産地の部分の「マルチリージョン」とか 、コクの「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
とエラーが表示されました。