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

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

# 開発環境と事前状況
### バージョン
・`ruby 2.6.5`
・`rails 6.0.3.4`
・`mysql 14.14`
### 事前状況
・商品の購入を行うための、ItemBuyモデルを構築済み

```ruby: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を導入済み

```ruby:Gemfile
  gem 'rspec-rails', '~> 4.0.0'
  gem 'factory_bot_rails'
  gem 'faker'
  # bundle install実行済み
```
```ruby: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
```
```ruby: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/859156/d858f59d-1200-0a17-0236-a19e70e0f6e4.png)
## 仮説
さて、エラー文を読むと、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/859156/86662a15-dce3-e7a8-8492-d9b2d8f7ffc7.png)
「Mysql」エラーが発生しているようでした。
DBとのデータのやり取りの際に何か起きているのかな・・・とこの時は思いましたが、itを一つ追加するごとにテストを実行し、確認しながら実装していたので、itの中身自体に問題があるわけではなさそうでした。

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

```ruby:spec＞models＞item_buy_spec.rb
    user = FactoryBot.create(:user)
    item = FactoryBot.create(:item)
```
ここが原因になっているのかなとも推察できました。

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

```ruby
@item_buy = FactoryBot.build(:item_buy, user_id: user.id, item_id: item.id)
```
この一つだけだったので、今まで問題はなかったのですが、user,itemという二つの変数を用意したことにより、

```ruby
    user = FactoryBot.create(:user)
    item = FactoryBot.create(:item)
    @item_buy = FactoryBot.build(:item_buy, user_id: user.id, item_id: item.id)
```
合計３回分の処理を事前に行わなければならなくなりました。
つまり、「userのFactoryBotをcreateして、itemのFactoryBotをcreateして、、、」としている間にテストコードのitを読みに行ってしまい、DBとのやりとりが間に合わなくなって、Mysqlエラーが発生した、という状況が起きていたようです。
## 解決策
この問題は、**sleepメソッドを使うことで解決できました**。
sleepメソッドとは、指定した時間、処理を停止することのできるrubyのメソッドの一つです。
例えばsleep(1)とすれば処理を１秒停止させられます。処理をとめて、データのやりとりを間に合うようにしようということです。
このsleepを0.1秒くらいの時間で`before do`の処理の直後に埋め込んで・・・

```ruby: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/859156/6d7ab19d-e9c8-1403-eca4-d2bc57640719.png)
今度はうまくいきました！！

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