0
0

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.

Rspec テストコード実行時のMysqlエラーについて

Last updated at Posted at 2021-03-17

はじめに

Ralisを使って実装中、テストコード実行時にMySQLエラーが発生しましたので、原因と解決策を記載しておきます。
同じ原因でつまづいてしまった方がいた時のお力になれればと思います。
なお、DB内の処理について、深い知見があるほどの技術力ではございません。それ故に、エラーの原因など、「厳密には違う」ということがあるかもしれません。
ご指摘いただければ修正いたしますので、どうか温かい目でご覧ください。

開発環境と事前状況

バージョン

ruby 2.6.5
rails 6.0.3.4
mysql 14.14

事前状況

・商品の購入を行うための、ItemBuyモデルを構築済み

app>models>item_buy.rb
class ItemBuy
  include ActiveModel::Model
  attr_accessor :postal_code, :from_id, :municipality, :house_number, :building_name, :tell, :user_id, :item_id, :token

  with_options presence: true do
    validates :token
    validates :postal_code, format: { with: /\A[0-9]{3}-[0-9]{4}\z/, message: 'Input correctly' }
    validates :from_id, numericality: { other_than: 1, message: 'Select' }
    validates :municipality
    validates :house_number
    validates :tell, format: { with: /\A\d{10,11}\z/, message: 'Input only number' }
    validates :user_id
    validates :item_id
  end
  #中略
end

・テスト環境として、Rspec、FactoryBot、Fakerを導入済み

Gemfile
  gem 'rspec-rails', '~> 4.0.0'
  gem 'factory_bot_rails'
  gem 'faker'
  # bundle install実行済み
spec>models>item_buy_spec.rb
require 'rails_helper'

RSpec.describe ItemBuy, type: :model do
  before do
    user = FactoryBot.create(:user)
    item = FactoryBot.create(:item)
    @item_buy = FactoryBot.build(:item_buy, user_id: user.id, item_id: item.id)

  end

  describe '商品購入' do
    context '商品購入がうまくいく時' do
      it '必須項目が全て入力されていれば購入できる' do
        expect(@item_buy).to be_valid
      end
      it '建物名が空でも購入できる' do
        @item_buy.building_name = ''
        expect(@item_buy).to be_valid
      end
    end

    context '商品購入がうまくいかない時' do
      it '郵便番号が空では購入ができない' do
        @item_buy.postal_code = ''
        @item_buy.valid?
        expect(@item_buy.errors.full_messages).to include("Postal code can't be blank")
      end
      it '郵便番号にハイフンがなければ購入ができない' do
        @item_buy.postal_code = '1111111'
        @item_buy.valid?
        expect(@item_buy.errors.full_messages).to include('Postal code Input correctly')
      end
      # 中略(実際にはこの他に10個の異常系itがありますが長くなるので削ります)
    end
  end
end
spec>factories>item_buy.rb
FactoryBot.define do
  factory :item_buy do
    postal_code   { '123-4567' }
    from_id       { 2 }
    municipality  { '相模原市' }
    house_number  { '大山1-1-1' }
    building_name { 'tower'}
    tell          { '09012345678' }
    token         { 'tok_abcdefghijk00000000000000000' }
  end
end

以上の状況下でbundle exec rspec spec/models/item_buy_spec.rbをターミナルで実行。
すると、下記のエラーが発生。テストはうまくいかなかった模様。
image.png

仮説

さて、エラー文を読むと、、、
image.png
「Mysql」エラーが発生しているようでした。
DBとのデータのやり取りの際に何か起きているのかな・・・とこの時は思いましたが、itを一つ追加するごとにテストを実行し、確認しながら実装していたので、itの中身自体に問題があるわけではなさそうでした。

他に考えられることとしては、商品情報と購入者情報もテストするために、item_buy_spec.rb内のbefore doの中に以下の二文を追加していました。

spec>models>item_buy_spec.rb
    user = FactoryBot.create(:user)
    item = FactoryBot.create(:item)

ここが原因になっているのかなとも推察できました。

原因

結論、仮説の通りbefore doの中の二文が原因でした。
before doによる処理はitの中身が読み込まれる前に実行されます。元々定義されていたのは、

@item_buy = FactoryBot.build(:item_buy, user_id: user.id, item_id: item.id)

この一つだけだったので、今まで問題はなかったのですが、user,itemという二つの変数を用意したことにより、

    user = FactoryBot.create(:user)
    item = FactoryBot.create(:item)
    @item_buy = FactoryBot.build(:item_buy, user_id: user.id, item_id: item.id)

合計3回分の処理を事前に行わなければならなくなりました。
つまり、「userのFactoryBotをcreateして、itemのFactoryBotをcreateして、、、」としている間にテストコードのitを読みに行ってしまい、DBとのやりとりが間に合わなくなって、Mysqlエラーが発生した、という状況が起きていたようです。

解決策

この問題は、sleepメソッドを使うことで解決できました
sleepメソッドとは、指定した時間、処理を停止することのできるrubyのメソッドの一つです。
例えばsleep(1)とすれば処理を1秒停止させられます。処理をとめて、データのやりとりを間に合うようにしようということです。
このsleepを0.1秒くらいの時間でbefore doの処理の直後に埋め込んで・・・

spec>models>item_buy_spec.rb

RSpec.describe ItemBuy, type: :model do
  before do
    user = FactoryBot.create(:user)
    item = FactoryBot.create(:item)
    @item_buy = FactoryBot.build(:item_buy, user_id: user.id, item_id: item.id)
    sleep(0.1)
  end

再びbundle exec rspec spec/models/item_buy_spec.rb実行!
image.png
今度はうまくいきました!!

このエラーを通じて感じたこと

テストコードに限らず、PCの処理速度は超速で、エラーの原因が「処理のやりとりに伴う速度」というのは今まであまり実感が湧いておらず、大体エラーが起きた時は自分のミスであることがほとんどで、コンマ〇〇秒の処理が問題になっているということはイメージもしていませんでした。
スペルミス、構文ミスなど、明らかなミスとは違って、このエラーは「実行した指示の中で今何が起きているのか」をちゃんと把握する重要性を感じさせてくれました。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?