RSpec導入してCircleCIに組み込んで見る

今までプロダクト優先で自動テストを書くのが億劫だったことを反省し、ついに意を決してRSpecを導入しました。
目標は

- 重要なモデルのバリデーションテスト
- コントローラー（request）の200確認・DB更新確認
- ボタンやリンク経由で画面遷移のシナリオテスト（フィーチャーテスト）
- CircleCIでRSpecのテストをデプロイ前に入れ込み、OKとならなければデプロイしないようにする

をすることです。もちろん究極は先にSpecを書いて、コードを実装するテストドリブン開発が理想ですが、今回後付になってしまったので現状の挙動を担保するという思想でテストを作成してます。
ただ、テストコードを書くと、意外なことにModelを端折ってしまって書いてないので、テストコード自体が動作しないというケースもあったりしてソース自体にも手を加える必要は出てきます。そうすると動いているソースに手を入れない的な昔からあるようなセオリーで押しきれないことがわかります。これもソースのメンテナンス性を上げるためのRSpecの効能と考えていいでしょう。

```ruby:Gemfile
group :development, :test do
  gem "rspec-rails" # RSpec本体
  gem "factory_bot_rails" # FactoryBot
  gem 'spring-commands-rspec' # RSpecのパフォーマンスを上げるGem
  gem 'capybara', '~> 2.15.2' # フィーチャーテストのためのCapibara
  gem 'rspec_junit_formatter' # CircleCI、JenkinsなどCIでSpec結果を読みやすくするためのGem
end
```
ちなみにRSpecは実行すると自動的に環境はRAILS_ENV=testになります。なので専用のデータベースをdevelopment用と別に作成する必要があります。またRails 5.1以降はテストで発生したデータを消すためのdatabase_cleanerは不要だそうです。これは5.1以降はテスト後にDBが自動的にRollbackしてデータを消すようになっているためだとか。データベースの作成に合わせてDBの設定にも以下を追加します。ちゃんとテスト用のDB名にしてあげるのがミソです。

```ruby:config/database.yml
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: test_db
```

また、config/environments/test.rbも作成します。環境別固有設定を施すためです。
ほとんどdevelopment環境と一緒なのでconfig/environments/development.rbをコピーするだけですが、自分はweb_consoleを使っていてRSpecが動かないことがあったので以下の設定を追加しています。

```ruby:config/environments/test.rb
  # web_console
  config.web_console.development_only = false
```

ここまでお膳立てしてようやく `bundle` とジェネレーターで環境を整えます。

```:bash
# RSpecインストール
$ bundle install

# 定型的な設定ファイルの作成
$ rails generate rspec:install
Running via Spring preloader in process 266
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb
```

rails_helper.rbとspec_helper.rbの役割の違いはよくわからないですが、ほとんどのSpecファイルにrails_helper.rbへの読み込みが入るので、こちらにもっぱらヘルパーの設定を加えます。
今回はdevise使うのでそれ用のヘルパーとダミーデータ作成のためのFactoryBotに設定のヘルパーを加えています。コードの最後の方に追加しました。

```ruby:spec/rails_helper.rb
  config.include Devise::Test::IntegrationHelpers, type: :request
  config.include FactoryBot::Syntax::Methods
```

Deviseを使用しているアプリケーションの場合、ヘルパー本体はこちらに書きます。

```ruby:spec/support/request_spec_helper.rb
module RequestSpecHelper
  include Warden::Test::Helpers

  def self.included(base)
    base.before(:each) { Warden.test_mode! }
    base.after(:each) { Warden.test_reset! }
  end

  def sign_in(resource)
    login_as(resource, scope: warden_scope(resource))
  end

  def sign_out(resource)
    logout(warden_scope(resource))
  end

  private

  def warden_scope(resource)
    resource.class.name.underscore.to_sym
  end
end
```

ベースとなる準備は以上です。

## Seed-fuでマスターデータを用意
アプリケーションにもよると思いますが、ある程度マスターデータが用意されてないと凝ったテストが出来ないという人はFactoryBotではなくてSeedでDBに値を事前に入れておくのがいいかと思います。Railsに元からあるseedよりもseed-fuの方が使い勝手良さそうなのでこちらをインストールします。データの自動投入に汎用的に使えるGemです。

```ruby:Gemfile
# Seed -fu
gem "seed-fu"
```
bundleで入れたら以下のファイルを作成します。

```ruby:db/seed.rb
Spree::Core::Engine.load_seed if defined?(Spree::Core)
Spree::Auth::Engine.load_seed if defined?(Spree::Auth)
```

あとは投入データを作成します。投入するためのスクリプトはこちらです。これがそのままDBの定義とseedの実行母体になります。

```ruby:db/fixtures/test/001_areas.rb
require 'csv'

csv = CSV.read('db/fixtures/test/001_areas.csv')
csv.each do |csvdata|
  Area.seed do |s|
    s.id = csvdata[0]
    s.prefstate_id = csvdata[1]
    s.photo = csvdata[2]
    s.lat = csvdata[3]
    s.lng = csvdata[4]
    s.created_at = csvdata[5]
    s.updated_at = csvdata[6]
  end
end
```
以下データ。

```ruby:db/fixtures/test/001_areas.csv
1,13,"",,,"2016-08-15 00:00:00","2016-08-15 00:00:00"
2,13,"",,,"2016-08-15 00:00:00","2016-08-15 00:00:00"
3,13,"",,,"2016-08-15 00:00:00","2016-08-15 00:00:00"
```
ちなみにdb/fixtures/の下のtestは環境名です。上記を設定したら

```
RAILS_ENV=test rails db:seed_fu
```

でデータ投入できますが、この際に環境ごとに投入データを変えられます。そのためにディレクトリで分けているのです。ちなみに実行はファイルの接頭辞の番号順に行われますので、外部キー制約など順番を考慮したい場合はうまく活用してください。

#### その他DB操作で便利なコマンド

```
RAILS_ENV=test rails db:reset #テーブル作り直し
RAILS_ENV=test bundle exec rake db:schema:load #テーブル作り直し
RAILS_ENV=test rails db:seed_fu FILTER=029_styles FIXTURE_PATH=./db/fixtures/test #フィクスチャーごとにデータ投入
```

## FactoryBotの作成
テストのたびにbeforeとかにDBのレコードを作成して用意するのが嫌（というかソースも見づらくて気持ち悪い）なので、データの生成をスマートにするためにFactoryBotというツールがあります。元々FactoryGirlという名称でしたが、ジェンダーコンシャスの流れを受けて今の名前になりました。既にGemfileに追加してインストールしているので後はファイルをspec/factories配下に書いていくだけです。必要となる各モデルごとに作成していきます。

```
FactoryBot.define do
  factory :user do
    sequence(:name) { |n| "TEST_NAME#{n}"} # 名前のバリデーションにかからないもの
    sequence(:email) { |n| "TEST#{n}@example.com"} # メールのバリデーションにかからないもの
    password  { 'password' } # パスワードのバリデーションにかからないもの
  end
end
```
番号などシーケンシャルなものを挿入したい場合はsequenceというのを使います。

```
FactoryBot.define do
  factory :review, class: Review do
    restaurant_id {1620}
    user_id {1}
    rating {5}
    title {'What a hell?'}
    image  {Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec/fixtures/image.jpg'))}
    comment {'It was yummy, indeed'}
  end
end
```

画像データをカラムに入れている場合は上記のように画像のダミーデータをspec/fixtures配下に置いてRack::Test::UploadedFile.newとします。

リレーションがあるモデル（has_one/has_many/belongs_to)の場合、

```ruby:app/models/menu.rb
class Menu < ActiveRecord::Base
  has_many :menu_translations, dependent: :destroy
```

```ruby:app/models/menu_translation.rb
class MenuTranslation < ActiveRecord::Base
  belongs_to :menu
```

以下のようにFactoryBotを作成します。

```ruby:spec/factories/menu.rb
FactoryBot.define do
  factory :menu, class: Menu do
    restaurant_id {1}
    menu_type  {5}
    image  {Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec/fixtures/image.jpg'))}
    last_update_user_id {1000}
    add_attribute(:public)  {'1'}

    association :restaurant
  end
end
```
ちなみにカラム名がRSpecの予約語とかぶってしまうものを使っている場合（aliasとかpublicなど）、add_attribute(:public)という書き方をすることが出来ます。

```ruby:spec/factories/menu_translation.rb
FactoryBot.define do
  factory :menu_translation_ja, class: MenuTranslation do
    menu_id  {}
    menuname {'パルミジャーノとケールのイタリアン菜園サラダ'}
    description {'ＳサイズとＭサイズがあります。'}
    price {1000}
    currency  {'円'}
    lang  {'ja'}

    association :menu
  end
end
```

associationでリレーションを定義します。

## Modelのスペック
deviseをログイン認証機構に用いた場合のモデルのテストは以下の通りです。
deviseで注意すべきはモデル（今回だとUser）に書いてないバリデーションもあり、そちらもテストするのか、それ以外のカスタムバリデーションだけテストするのかを考えてコードを記載します。デフォルトではメールアドレスとパスワードの存在チェックはdeviseに組み込まれているようでした。
それ以外は普通にバリデーションやメソッドについてテストを記載します。

```ruby:spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  let(:user) { build(:user)}

  describe 'Check Validations' do
    it 'default OK' do
      expect(user.valid?).to eq(true)
    end

    it 'if no password, then NG' do
      user.password = ''
      expect(user.valid?).to eq(false)
      expect(user.errors[:password]).to include 'パスワードを入力してください'
    end

    it 'if no name, then NG' do
      user.name = ''
      expect(user.valid?).to eq(false)
    end

    it 'if name exceed 120 letters, then NG' do
      user.name = '1234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567890000000000000123456789012345678901234567890'
      user.completed_at = Time.current
      expect(user.valid?).to eq(false)
    end
  end
end
```

こちらがModelの本体です。これをテストするためにSpecを記載しました。

```ruby:app/models/user.rb
class User < ApplicationRecord
  mount_uploader :pic, ImageUploader
  attr_accessor :current_password

  validates :name,
    length: { minimum: 1, maximum: 120 }, if: :is_registration?

  validates :name, presence: true
end
```
FactoryBotには正常系を作成しておいて、バリデーションのテストの中で異常データを挿入してvalid?を試すイメージです。

## Controllerのスペック
EverydayRails ‐ RSpecによるRailsテスト入門というRSpecの有名書籍によればコントローラーのテストはやがてなくなり、requestsのテストとして集約されるとのことです。いろいろな現場でテストを書かせてもらいましたが、contorollerにテストを書いているところは多いです。コントローラーにアクセスして、所定のキーワードがあるかどうか、関連するモデルの数が増えたり減ったりするかといったテストです。スマホアプリ構築の流れでWebAPIの開発というパターンも多いことからこれをテストするためにrequestsにテストを書くということのようですが、ここにControllerも集約されるぽいです。認識違っていたらすみません。
以下は弊社サービスVegewelでレストランガイドをやっておりまして、ページを表示、レビューの投稿をテストするコードを書いたものです。

```ruby:spec/requests/restaurant_spec.rb
require 'rails_helper'

RSpec.describe RestaurantController, type: :request do
  describe "GET index" do
    before do
      @restaurants = FactoryBot.create(:restaurant)
    end

    it "responds successfully" do
      get "/restaurant"
      expect(response).to be_success
    end
  end

  describe "GET show on restaurant detail" do
    it "responds successfully" do
      get "/restaurant/#{@restaurants.id}"
      expect(response).to be_success
    end
  end
end
```
コントローラーのテストの基本の作りはシンプルです。
ルーティングに合わせてget, post, patch, put, delete...を発効して結果をexpectのtoに期待値を書いていくというものです。

```
require 'rails_helper'

RSpec.describe ReviewsController, type: :request do
  describe "POST review" do
    let(:review) { FactoryBot.create :review }

    before do
      @user = FactoryBot.create(:user)
      @restaurant = FactoryBot.create(:restaurant)
      @user.confirm # deviseのメール認証が必要なケースでは必須
      sign_in @user
    end

    it "responds successfully" do
      expect{
        post '/reviews', params: { user_id: @user.id, restaurant_id: @restaurant.id, review: review }
      }.to change(Review, :count).by(1)
    end
  end
end
```
post など更新がかかるアクションにはパラメータを設定しておきます。
Factoryの使い方としては事前評価と遅延評価という考え方があり、もともとAだったものがBになるということをテストするならベースとなるデータを事前評価ということでlet!でデータをcreateなどしておいて、更新の結果で増減があるか確かめるという流れです。
単純にレストランデータを作って該当URL(restaurant/1234みたいな）もので表示するだけなら遅延評価でletでデータを作成するので十分かと思います。

ちなみにフォームは確認画面とかはなく、登録・更新ボタン一発で変更が加わるようにしています。
追加したら当該モデルの数が＋１、更新したら変更後の該当カラムが想定通りに変更されていることを確認するようなコードにしています。

### リレーションのあるモデルを同時に更新する場合のコントローラーのSpecの書き方
コントローラーに以下のようなparamsの設定があるとします。

```ruby:app/controllers/menu_controller.rb
private
  def menu_params
    params.require(:menu).permit(:id, :restaurant_id , :image1, :image2, :image3, :last_update_user_id, :public, menu_type_list: [], menu_translations_attributes:[:id, :menu_id, :menuname, :description, :price, :currency, :lang])
  end
```

パラメータに親子関係を設定したものとFactoryの設定にassciationを仕込ませておいた上で以下のようなテストコードを書きます。

```ruby:spec/requests/menu_spec.rb
  # 新規作成
  describe "POST /menu with authentication" do
    before do
      @admin_user = FactoryBot.create(:admin_user)
      sign_in @admin_user
    end

    it "Create new menu for Japanese" do
      menu_translation_params_ja = {
        menu_translations_attributes: {
          "0": FactoryBot.attributes_for(:menu_translation_ja)
        }
      }
      menu_params = FactoryBot.attributes_for(:menu, restaurant_id: restaurant.id).merge(menu_translation_params_ja)
      expect{
        post '/menu', params: { menu: menu_params }
      }.to change(Menu, :count).by(1)
    end

  # 更新
  describe "PATCH /menu with authentication" do
    let(:restaurant) { FactoryBot.create(:restaurant) }
    let!(:menu) { FactoryBot.create(:menu, restaurant_id: restaurant.id) }

    before do
      @admin_user = FactoryBot.create(:admin_user)
      sign_in @admin_user
    end

    it "Update menu for Japanese" do
      menu_translation_params_ja = {
        menu_translations_attributes: {
          "0": FactoryBot.attributes_for(:menu_translation_ja, menuname: "ベジプレート（大豆ミートの鶏から風）", price: 500, menu_id: menu.id)
        }
      }
      menu_params = FactoryBot.attributes_for(:menu, id: menu.id, restaurant_id: restaurant.id).merge(menu_translation_params_ja)
      patch "/menu/#{menu.id}", params: {menu: menu_params}
      expect(menu.menu_translations.first.menuname).to eq("ベジプレート（大豆ミートの鶏から風）")
      expect(menu.menu_translations.first.price).to eq(500)
    end
  end
```

上記同様にフォームは確認画面とかはなく、登録・更新ボタン一発で変更が加わるようにしています。
追加したら当該モデルの数が＋１、更新したら変更後の該当カラムが想定通りに変更されていることを確認するようなコードにしています。

以上でコントローラーのテストについて記載しました。

## サイトの表側のふるまいをテストするフィーチャーテスト
Capybaraを用いてサイト上の動作をシミュレートしたテスト（フィーチャーテスト）をすることが出来ます。
RSpecをやるまではテストのコードを書くというとこっちのイメージでした。Seleniumはこちらのイメージと言えるでしょう。
試しにサイトのトップページからリンクをたどって別ページに飛んで、そこから検索するというスクリプトを書いてみます。

```ruby:spec/features/projects_spec.rb
require 'rails_helper'

RSpec.feature "Projects", type: :feature do
  scenario "Vegewel success scenario in English" do
    # Vegewel TOP
    visit '/en'
    expect(page).to have_content "Vegewel restaurant guide"
    expect(page).to have_content "Vegewel Style"

    # Restaurant Page
    click_link ("Restaurant")
    expect(page).to have_content "Tasty & Healthy Restaurants"
    expect(page).to have_content "VESPERA"

    # Search Restaurant 
    fill_in('q[g][1][restaurant_search]', with: 'Cafe')
    check('q[g][0][veganmenu_eq]')
    click_button ("Search")
    expect(page).to have_content "Cafe*teria HANIWA"
  end
end
```
ちなみに
特定のページを開く：visit
リンクを踏む：click_link
ボタンを押す：click_button
チェックボックスを押す：check
テキストボックスに入力：fill_in
で制御できます。ボタン押したり、リンククリックしたりの場所の特定はIDなどのセレクタや、ラベルなどが表示されていればその文言で指定することが出来ます。可読性を上げるためにボタンのラベルに指定するとかもアリでしょう。

その他の動作に関してはこちらを参照して、色々シミュレートしてみることが出来るかと思います。
https://qiita.com/morrr/items/0e24251c049180218db4
ご確認ください。

## RSpecのテスト
```
$ bundle exec rspec
```

で全ての（モデル、コントローラー、フィーチャー）のテストは実行されます。個別に実行したければファイルを指定すればOKです。テスト通ればSuccessと表示され、NGだとエラー内容が表示されます。

## CircleCIにRSpecのテストを組み込み
もともとBitbucketの特定ブランチ（Staging、Master）にソースをコミットしたらデプロイする仕組みを構築していました。そこにRSpecのテストを追加してみました。以下.circleci/config.ymlです。
本当は全体はかなり長いのですが、RSpec組み込んだところだけフォーカスしています。

```ruby:.circleci/config.yml
version: 2.1
orbs:
  ruby: circleci/ruby@0.1.2

jobs:
  build:
    working_directory: ~/xxxx
    parallelism: 1
    docker:
      - image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37

    steps:
      - checkout
      - run:
          （諸々Deployコマンド）
  rspec:
    parallelism: 3
    docker:
      - image: circleci/ruby:2.5.0-node-browsers
        environment:
          - BUNDLER_VERSION: 1.16.1
          - RAILS_ENV: 'test'
      - image: circleci/mysql:5.7
        environment:
          - MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
          - MYSQL_ROOT_HOST: '127.0.0.1'
    steps:
      - checkout
      - restore_cache:
          key: v1-bundle-{{ checksum "Gemfile.lock" }}
      - run:
          name: install dependencies
          command: |
            gem install bundler -v 2.0.2
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      - save_cache:
          key: v1-bundle-{{ checksum "Gemfile.lock" }}
          paths:
            - ~/circleci-demo-workflows/vendor/bundle

      # Database setup
      - run: mv ./config/database.yml.ci ./config/database.yml
      - run:
          name: Databasesetup
          command: |
             bundle exec rake db:create
             bundle exec rake db:schema:load
             bundle exec rake db:seed_fu
      # run tests!
      - run:
          name: Run rspec
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"

            bundle exec rspec \
              --format progress \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES
      # collect reports
      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

workflows:
  version: 2
  build-n-deploy:
    jobs:
      - rspec:
          filters:
            branches: #Spec走らせるブランチを限定（これないとフィーチャーブランチのコミット＆Pushで勝手にCircleCIが走ってしまう）
              only:
                - staging
                - master
      - build:
          requires:
            - rspec # rspecしてからDeployするように
          filters:
            branches:
              only:
                - staging
                - master
```

こんな感じでJobを分割してRspecとしたところに
- テスト環境構築
- Seedデータを投入
- RSpecテスト実行
を実行しています。
workflowsのところに全体的な流れとして、先にRSpecを流してからOKならDeployを実行する流れにしています。
あと、これが最も重要なポイントですが、これを実行するためにCircleCIに課金しました。 **1ヶ月$30** です。
複数Job実行にするのは無料プランでは出来なかったからです。この金額で25000ポイントもらえるので、各リソースの利用状況ごとにポイントが減らされる仕組みになっています。
弊社のサービスは1回のデプロイで300〜400ポイントくらい消費するので実行は慎重にやることにしています。ポイントが無くなってきたら、自動的に＋＄３０してポイント追加になります。
課金するとJobの並行実行も可能です。複数の同時デプロイなどに使えるかと思うので使い方はご検討ください。

## その他トラブル対応
### 特定のURLで200が返ってこなくてエラー
例えば以下のようなテストを書いたとして、エラーになって返ってこないケースです。

```
    it "Login returns 200" do
      get index_path
      expect(response).to have_http_status "200"
    end
```
エラーはこんな感じです。

```
Failure/Error: expect(response.status).to eq 200

       expected: 200
            got: 302
```
つまりリダイレクトしているってことですよね？調べたところapplication_controller.rbでdeviseログイン時やhttp->httpsリダイレクトで302をやらかしていました。また、Staging環境ではBasic認証かけたりとか色々やっていて、条件分岐に環境変数を使っていたことからtest環境へのケアが出来ていなかったのも要因でした。
上記エラーになったらapplication_controller.rbのbefore_actionを片っ端からコメントアウトして1個づつつけたり外したりして試してみるのが吉です。

### アプリケーションFQDNがwww.example.comとなってしまう？bad URIも発生
テストに以下のようなコードを書いたところbad URIとなり、しかもアプリケーションはwww.example.comというFQDNになってました。

```
require 'rails_helper'

RSpec.describe "Restaurants", type: :request do
  describe "GET /index" do
    it "index responds successfully" do
      get :index
      expect(response).to be_success
    end
  end
end
```
以下エラー

```
URI::InvalidURIError:
  bad URI(is not URI?): http://www.example.com:80index
```
これは `get :index` を `get '/'` とすることでテストが通りました。:indexとすることで文字列にindexを足してしまうようです。また、www.example.comはRailsでActionPackの中にTestのSessionを司る箇所があり、そこで `DEFAULT_HOST='www.example.com'` と定義されていて、これがRSpec内でURLに指定がなければ自動的に設定されてしまうのが原因でした。ただ、通常のテストではwww.example.comが問題になることがなく「そういうもの」と考えて特に気にしなくて良い模様です。自分の場合はpryで何度止めてもこのFQDNがサーバ名として定義されているので焦りましたが、RSpecのメンターしてくれた先生に聞いたら気にしなくてOKと言われて安堵しました。

### CircleCIでMySQLのDockerImage構築中にDBセットアップを始めようとしてエラーになる
![4ad72481bdb0fd42bbe0a80b1947c175.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1593/ad824ce5-8618-b67b-af8c-f8b2c49517b8.png)

この図で言うところの `Container circleci/mysql5.7` のところが終わっていないのに、DBのセットアップが始まってしまうということがありました。そもそも当初CircleCIは課金していないので基本的に複数Jobを回すようにしておらず、以下のように環境構築、RSpec実行、AWSCLIの設定、ElasticBeanstalkのデプロイまでまとめてやろうとしていました。
その中でいうとimage: circleci/mysql:5.7のところが長い時間かかっているのですが、これと同期をとる方法がわかりませんでした。

```
version: 2
jobs:
  build:
    working_directory: ~/xxxx
    parallelism: 1
    shell: /bin/bash --login
    environment:
      CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
      CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
    docker:
    - image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
    - image: circleci/ruby:2.5.0-node-browsers
      environment:
        - BUNDLER_VERSION: 1.16.1
        - RAILS_ENV: 'test'
    - image: circleci/mysql:5.7
      environment:
        - MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
        - MYSQL_ROOT_HOST: '127.0.0.1'
    steps:
      - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
      - run:
          working_directory: ~/xxxx
          command: pip install pip==20.3.4
      - run:
          working_directory: ~/xxxx
          command: pip install urllib3==1.26
      - run:
          working_directory: ~/xxxx
          command: pip install awsebcli --upgrade --user
      - checkout
      - run:
          name: install dependencies
          command: |
            gem install bundler -v 2.0.2
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      # Database setup
      - run: mv ./config/database.yml.ci ./config/database.yml
      - run:
          name: Databasesetup
          command: |
             bundle exec rake db:create
             bundle exec rake db:schema:load

      # run tests!
      - run:
          name: Run rspec
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"

            bundle exec rspec \
              --format progress \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES

      # collect reports
      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

      - run:
          name: Deploy
          command: |
            if [ "${CIRCLE_BRANCH}" == "master" ]; then
              echo "Deploy production"
              eb deploy Vegewel-production
            else
              echo "Deploy staging"
              eb deploy vegewel-staging
            fi

workflows:
  version: 2
  build-n-deploy:
    jobs:
      - build:
          filters:
            branches:
              only:
                - staging
                - master
```

最終的には課金して複数JobでRSpecとデプロイという形にしましたが、CircleCIのサポートによればDockerizeを使って待機させることもできるそうです。こちらのドキュメントを参照ください。
https://circleci.com/docs/ja/2.0/databases/#dockerize-%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E4%BE%9D%E5%AD%98%E9%96%A2%E4%BF%82%E3%81%AE%E5%BE%85%E6%A9%9F

### acts_as_taggable_onを使った箇所のテスト
動的なタグ生成のためにacts_as_taggable_onを使って開発しているところもあると思います。非常に便利で私もよく利用しています。
これのテストですが、自分は以下のようにしました。前提として料理の種類をCuisineとしていて、コードのID：１に対してCuisineTranslationの日本語（lang:ja）に”和食”、英語（lang:en）に”Japanese”のように２レコード設定しているようなデータ構造とお考えください。また、タグ用のテーブルはtags, taggingsの２つです。

```ruby:app/models/cuisine.rb
class Cuisine < ActiveRecord::Base
  ActsAsTaggableOn::Tagging.table_name = 'taggings'
  ActsAsTaggableOn::Tag.table_name = 'tags'

    has_many :cuisine_translations
end
```

```ruby:app/models/cuisine_translation.rb
class CuisineTranslation < ActiveRecord::Base
        belongs_to :cuisine
        belongs_to :restaurant

        scope :with_lang , -> { where(lang: I18n.locale )}
end
```

これを扱うRestaurantモデルは

```ruby:app/models/restaurant.rb
class Restaurant < ActiveRecord::Base
  ActsAsTaggableOn::Tagging.table_name = 'taggings'
  ActsAsTaggableOn::Tag.table_name = 'tags'

  acts_as_taggable_on :stations, :lines, :cuisines
```

こんな感じです。Factoryの設定はTagに対して行います。

```ruby:spec/factories/tag.rb
FactoryBot.define do
  factory :cuisine_list, class: ActsAsTaggableOn::Tag do
    name {Cuisine.first.id}
  end
```

として、RestaurantのFactoryに以下のようにかけば動くはずです。

```ruby:spec/factories/restaurant.rb
FactoryBot.define do
  factory :restaurant, class: Restaurant do
    station_list {[FactoryBot.build(:station_list)]}
    line_list {[FactoryBot.build(:line_list)]}
    cuisine_list {[FactoryBot.build(:cuisine_list)]}
```

## 最後に
かなり長い文章になってしまいました。こういうときは記事を分けるのがいいかなと思いつつ、大半は他のQiitaにも書いてそうなことなので付加価値出そうとしてボリューミーになってしまいました。多少なりともお役に立つ内容があれば幸いです。