#今回やること
前回、やっとまともに単体テストをやって、気を良くしたので親子関係のあるモデルの画像投稿の単体テストをやろうと思いました。
本当は「子モデルの単体テスト」だけにしたかったのですが、子モデルのカラムには画像保存のカラムしかなかった。
なので半強制的に画像の投稿テストまでやることになりました。
色々と勉強になったので備忘録として記載します。
#動作環境
ruby 2.5.1
rails 5.2.4.2
carrierwave 2.1.0
mini_magick 4.9.5
#下準備
ここまでの環境は前々回とか前回の記事の下準備をご覧ください。
#今回のテスト環境の準備
起動時間が早くなるらしいと聞いたので 'spring-commands-rspec' を追加します。(必須ではありません。)
また、後々、データの確認に 'pry-rails' が必要になるとお告げがあったので、これも追加しておきます。
####gemのインストール
(以下を追記)
gem 'spring-commands-rspec'
gem 'pry-rails'
$ bundle install
####rspecファイルを作成する
$ bin/rails g rspec:model image
####テスト用インスタンスを作成する
FactoryBot.define do
factory :image do
image {"image.jpg"} #image は 画像ファイル想定なので "image.jpg" を代入します。
association: item
end
end
「モデル名とカラム名だけ変えて、あとは前回と同じでいいでしょ。」みたいなノリで作りました。
※ associationを書くことで、前回の記事で作成したitemのインスタンスをitem_idに入れてくれるそうです。(便利)
####テストコードを記述
require 'rails_helper'
RSpec.describe Image, type: :model do
describe '#create' do
let(:image) {build(:image)}
context 'can save' do
it "is valid with a image,item_id" do #image に "image.jpg" が入っていて、item_idはassociationで補うため
expect(image).to be_valid #成功する(はず)
end
end
context 'can not save' do
it "is invalid without a image" do
image.image = "" #image が 空の場合
expect(item).to be_invalid #失敗する(はず)
end
it "is invalid without a item_id" do
image.item_id = "" #item_id が 空の場合
expect(item).to be_invalid #失敗する(はず)
end
end
end
end
「モデル名とカラム名だけ変えて、あとは前回と大体同じでいいでしょ。」みたいなノリで(以下略)
####忘れずにvalidationを記述
class Image < ApplicationRecord
(中略)
validates :image,:item_id,presence: true
end
かろうじて思い出した。
#テストの実行結果
「前回の失敗踏まえてvalidationも書いたし、一発でテスト完了するまであるのでは?」と思いながら、いざ、実行。
$ bundle exec rspec spec/models/image_spec.rb
Image
#create
can save
is valid with a image,item_id (FAILED - 1)
can not save
is invalid without a image
is invalid without a item_id
Failures:
1) Image#create can save is valid with a image,item_id
Failure/Error: expect(image).to be_valid
expected #<Image id: nil, item_id: nil, image: nil, created_at: nil, updated_at: nil> to be valid, but got errors: Image can't be blank, Item can't be blank
# ./spec/models/image_spec.rb:8:in `block (4 levels) in <top (required)>'
Finished in 0.40532 seconds (files took 5.77 seconds to load)
3 examples, 1 failure
Failed examples:
rspec ./spec/models/image_spec.rb:7 # Image#create can save is valid with a image,item_id
全然ダメでした。
#原因を探る
ただ情報は落ちました。
but got errors: Image can't be blank, Item can't be blank
曰く、「image
とitem
がnil
なんで作れません。」とのこと。
は? インスタンスちゃんと作ったよね?
binding.pry
を記述して確認してみる。
お告げに従って'pry-rails'
を入れておいて良かった!
(中略)
context 'can save' do
it "is valid with a image,item_id" do
binding.pry
expect(image).to be_valid
end
end
(後略)
$ bundle exec rspec spec/models/image_spec.rb
3: RSpec.describe Image, type: :model do
4: describe '#create' do
5: let(:image) {build(:image)}
6: context 'can save' do
7: it "is valid with a image,item_id" do
=> 8: binding.pry
9: expect(image).to be_valid
10: end
11: end
12: context 'can not save' do
13: it "is invalid without a image" do
[1] pry(#<RSpec::ExampleGroups::Image::Create::CanSave>)> image
=> #<Image:0x00007fecd2c898c8 id: nil, item_id: nil, image: nil, created_at: nil, updated_at: nil>
確かに、item_id
もimage
もnil
です。
#Item_idを作る
調べたところ、どうもbuild
では、item_id
を生成してくれないようです。
そこでbuild
をid
を生成してくれるらしいbuild_stubbed
へ変更します。
(中略)
let(:image) {build_stubbed(:image)}
context 'can save' do
it "is valid with a image,item_id" do
binding.pry
expect(image).to be_valid
end
end
(後略)
$ bundle exec rspec spec/models/image_spec.rb
3: RSpec.describe Image, type: :model do
4: describe '#create' do
5: let(:image) {build_stubbed(:image)}
6: context 'can save' do
7: it "is valid with a image,item_id" do
=> 8: binding.pry
9: expect(image).to be_valid
10: end
11: end
12: context 'can not save' do
13: it "is invalid without a image" do
[1] pry(#<RSpec::ExampleGroups::Image::Create::CanSave>)> image
=> #<Image:0x00007fd9f98b7510 id: 1002, item_id: 1001, image: nil, created_at: Fri, 01 May 2020 13:05:44 UTC +00:00, updated_at: Fri, 01 May 2020 13:05:44 UTC +00:00>
Image
#create
can save
is valid with a image,item_id (FAILED - 1)
can not save
is invalid without a image
is invalid without a item_id
Failures:
1) Image#create can save is invalid with a image,item_id
Failure/Error: expect(image).to be_valid
expected #<Image id: 1002, item_id: 1001, image: nil, created_at: "2020-05-01 13:09:02", updated_at: "2020-05-01 13:09:02"> to be valid, but got errors: Image can't be blank
# ./spec/models/image_spec.rb:8:in `block (4 levels) in <top (required)>'
Finished in 0.17615 seconds (files took 2.58 seconds to load)
3 examples, 1 failure
Failed examples:
rspec ./spec/models/image_spec.rb:7 # Image#create can save is valid with a image,item_id
ちゃんとitem_id
が作成され、エラーもbut got errors: Image can't be blank
のみになりました。
#インスタンスに画像ファイルを代入する。
どうもファイル名をstring型で与えるだけでは、carrierwave
のvalidation
に引っかかってnil
になってしまうようです。
image
へファイルを代入するには以下へコードを書き換える必要がありました。
FactoryBot.define do
factory :image do
image { Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec/fixtures/image.jpg'))}
#記載したpathに画像ファイルを配置する必要あります。(ここでは'spec/fixtures/image.jpg')
association :item
end
end
$ bundle exec rspec spec/models/image_spec.rb
3: RSpec.describe Image, type: :model do
4: describe '#create' do
5: let(:image) {build_stubbed(:image)}
6: context 'can save' do
7: it "is valid with a image,item_id" do
=> 8: binding.pry
9: expect(image).to be_valid
10: end
11: end
12: context 'can not save' do
13: it "is valid without a image" do
[1] pry(#<RSpec::ExampleGroups::Image::Create::CanSave>)> image
=> #<Image:0x00007f9c93dbd6e8 id: 1002, item_id: 1001, image: nil, created_at: Fri, 01 May 2020 13:22:21 UTC +00:00, updated_at: Fri, 01 May 2020 13:22:21 UTC +00:00>
あれ? image: nil
?
念のため、image.image
でparameter
を確認します。
[2] pry(#<RSpec::ExampleGroups::Image::Create::CanSave>)> image.image
=> #<ImageUploader:0x00007f9c8fab4748
@cache_id="1588339341-125220018744846-0001-9913",
@cache_storage=#<CarrierWave::Storage::File:0x00007f9c94889c70 @cache_called=nil, @uploader=#<ImageUploader:0x00007f9c8fab4748 ...>>,
@file=
#<CarrierWave::SanitizedFile:0x00007f9c93d83128
@content=nil,
@content_type="text/plain",
@file="/Users/sggk.mrsk/projects/frima_sample/public/uploads/tmp/1588339341-125220018744846-0001-9913/test.jpg",
@original_filename="test.jpg">,
@filename="test.jpg",
@identifier=nil,
@model=#<Image:0x00007f9c93dbd6e8 id: 1002, item_id: 1001, image: nil, created_at: Fri, 01 May 2020 13:22:21 UTC +00:00, updated_at: Fri, 01 May 2020 13:22:21 UTC +00:00>,
@mounted_as=:image,
@original_filename="test.jpg",
@staged=true,
@versions={}>
ホッ、よかった。image: nil
は見せかけでした。
#結果
$ bundle exec rspec spec/models/image_spec.rb
Image
#create
can save
is valid with a image,item_id
can not save
is valid without a image
is valid without a item_id
Finished in 0.68652 seconds (files took 5.75 seconds to load)
3 examples, 0 failures
なんとか成功しました!
#教訓
テストって奥が深い。
今回は以上です。
#参考にさせていただいた記事