Ruby 3.x, Rails 6.x, MySQL 8.x のプロジェクトを CircleCI に設定し RSpec と Rubocop を実施する。

# 概要

前回の記事 [Ruby 3.x, Rails 6.x, MySQL 8.x の Docker 環境構築。](https://qiita.com/goya-hideaki/items/2597b39b966cb37ec9e1) を引き継いで、プロジェクトを CircleCI に設定し、GitHub リポジトリへの push 時に RSpec と Rubocop を自動実施するように設定します。

基本的に以下の CircleCI 公式ドキュメントを参考にしていますので、初期設定の方法などはそちらをご確認ください。
大きく違うのは、Ruby on Rails で MySQL に対応した点です。

- [入門ガイド](https://circleci.com/docs/ja/2.0/getting-started/)
- [言語ガイド: Ruby](https://circleci.com/docs/ja/2.0/language-ruby/)
- [データベースの構成例](https://circleci.com/docs/ja/2.0/postgres-config/)

# 前提

下記の環境が準備されていること。

- GitHub のアカウントを持っていること（無料版でも大丈夫）
- 上記の GitHub アカウントに紐づいた CircleCI のアカウントを持っていること（無料版でも大丈夫）
- Docker を利用した Ruby on Rails のプロジェクトが作成されていること

今回は CircleCI の設定が主になるため、Docker や RSpec、Rubocop の細かい説明は省きます。

# RSpec と Rubocop の組み込み

Gemfile を編集し、:development と :test 環境に rubocop（ついでに pry-byebug と factory_bot_rails）を、:test 環境に rspec-rails と database_cleaner-active_record を追加します。

gem についてはプロジェクトごと、必要に応じて対応してください。

## .Gemfile
```:.Gemfile

group :development, :test do
  # 略
  gem 'pry-byebug'
  gem 'factory_bot_rails'

  # https://github.com/rubocop-hq/rubocop
  gem 'rubocop', '~> 1.10.0', require: false
end

group :test do
  # 略
  gem 'rspec-rails', '4.0.2'

  gem 'database_cleaner-active_record'
end
```

設定後、`bundle install` と RSpec のインストール `rails g rspec:install`、rubocop の設定ファイル作成 `bundle exec rubocop --auto-gen-config` を実行します。
前回のプロジェクトは Docker で作成していたため、その場合は以下のように docker-compose を使用します。
なお、minitest 用の test フォルダが不要な場合は削除してください。

```console
$ docker-compose run app bundle install
$ docker-compose run app rails g rspec:install
$ docker-compose run app bundle exec rubocop --auto-gen-config
```

## config/application.rb
Scaffold 実行時に RSpec のスケルトンを自動生成するかどうかの設定を APP::Application クラス内に記入します。自動生成が不要な場合は `g.test_framework false` とします。
また、FactoryBot についても書いておきます。

```config/application.rb
module App
  class Application < Rails::Application
    config.generators do |g|
      g.test_framework :rspec,
                       feature_specs: true,
                       fixtures: false,
                       view_specs: true,
                       helper_specs: false,
                       routing_specs: false,
                       controller_specs: false,
                       request_specs: true,
                       system_specs: true

      g.factory_bot false
      g.factory_bot dir: 'spec/factories'
    end
  end
end
```

## rubocop ファイル
`bundle exec rubocop --auto-gen-config` を実行することで .rubocop.yml と .rubocop_todo.yml ファイルが作成されます。

```console
.
├── rubocop.yml
└── rubocop_todo.yml
```

実際の rubocop の運用については公式サイトを確認してください。

cf. [rubocop / rubocop](https://github.com/rubocop/rubocop)

大まかな手順としては、`rubocop --auto-gen-config` 実施時にエラーになった箇所が _todo.yml の方に記載されるので、そこから修正するつもりがない設定を .rubocop.yml に移動し、それ以外は _todo.yml からは削除し、エラー要素を実ファイルから修正していくことになります。

# CircleCI の設定

本題の CircleCI の設定となります。

## .circleci/config.yml

```console
.
└── .circleci/
    └── config.yml
```

.circleci/config.yml は Docker の docker-compose.yml と少し似ています。
今回はリポジトリへの push 時に RSpec と rubocop を自動実施するためのモノなので、設定する環境は test となります。

注意点としては、この設定では MySQL および Docker の仕組み上、CircleCI 実施時に MySQL のコンテナで頻繁に `mbind: Operation not permitted` という警告が出ますが、いまのところ無視するしかないようです。
（Docker だけの対応では `cap_add: - SYS_NICE` を付加することで警告が非表示になるようですが、CircleCI では設定できません）

cf. [circleci　コンテナの立ち上げ失敗＆ビルドがキャンセルされる？](https://qiita.com/ashketcham/items/d870c035feaaac590270)

また、イメージのタグは以下の一覧から 2021/02 現在の安定最新版を探しました。
cf. https://circleci.com/docs/ja/2.0/docker-image-tags.json

```.circleci/config.yml
version: 2.1

orbs:
  ruby: circleci/ruby@1.1.2
  node: circleci/node@4.2.0

jobs:
  build:
    docker:
      - image: cimg/ruby:3.0.0-node
    steps:
      - checkout
      - ruby/install-deps
      - node/install-packages:
          pkg-manager: yarn
  test:
    docker:
      - image: cimg/ruby:3.0.0-node
        environment:
          RAILS_ENV: test
      - image: circleci/mysql:8.0.4
        name: db
        command: mysqld --default-authentication-plugin=mysql_native_password
        environment:
          MYSQL_DATABASE: app_test
          MYSQL_USER: user
          MYSQL_PASSWORD: password
    environment:
      BUNDLE_JOBS: "3"
      BUNDLE_RETRY: "3"
    steps:
      - checkout
      - ruby/install-deps
      - node/install-packages:
          pkg-manager: yarn
      - run:
          name: Wait for DB
          command: dockerize -wait tcp://db:3306 -timeout 1m
      - run:
          name: Database setup
          command: bundle exec rails db:schema:load --trace
      - ruby/rspec-test
      - ruby/rubocop-check

workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build
```

ここまで追加したものを登録済みの GitHub に push しておきます。

# CircleCI サイトでのプロジェクトの設定

https://app.circleci.com/ にログインし、GitHub と連携したプロジェクト（リポジトリ）を設定します。
詳細な手順は [入門ガイド](https://circleci.com/docs/ja/2.0/getting-started/) を確認してください。

GitHub と連携できている場合は Projects ページにリポジトリの一覧が表示されるため、対象となるもので「Set Up Project」ボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1144449/2f161f4b-fa42-8a15-17cf-bec20dbe4bb0.png)

その後、すでに config.yml は設定済みのため、次の画面で「Use Existing Config」ボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1144449/9e93ff12-19e7-fcbd-ad16-1739f29c6b3e.png)

簡単な警告ダイアログが出ますが、「Start Building」ボタンを押下して先に進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1144449/455d5ca9-198c-adf8-02fb-c65da26f3658.png)

この時点で、設定に問題がなければ CircleCI の Dashboard に最初のテスト結果が「Success」として表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1144449/60ebe2da-0d3f-4c42-474b-99b90165ac16.png)

また、GitHub 側のリポジトリにも安心のチェックマークが表示されるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1144449/aced430b-6897-5e51-035d-cc13f499c933.png)

# RSpec の実装

rubocop は書式の問題なのでさておき、RSpec の動作確認だけ簡単に説明します。

## 前提

前回の最後に scaffold で作成した users テーブルがあることとします。

```console
$ docker-compose run app rails g scaffold user name:string email:string
```

## バリデーション追加

User モデルにバリデーションを追加します。

```app/models/user.rb
class User < ApplicationRecord
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

  validates :name, presence: true, length: { maximum: 250 }
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }

  before_save { self.email = email.downcase }
end
```

## FactoryBot

ユニットテストで使用するテストデータを事前に設定しておきます。

```spec/factories/users.rb
FactoryBot.define do
  factory :user do
    sequence(:name) { |n| "test name#{n}" }
    sequence(:email) { |n| "test#{n}@example.com" }

    trait :empty do
      name { nil }
      email  { nil }
    end

    trait :invalid_email do
      sequence(:email) { |n| "test#{n}" }
    end
  end
end
```

## モデルテスト

（ごく単純なものとなりますが、）モデルのテストを設定します。

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

RSpec.describe User, type: :model do
  describe '#valid?' do
    let!(:user) { described_class.new(attributes) }
    subject { user.valid? }

    context 'successful' do
      let!(:attributes) { attributes_for(:user) }
      it { is_expected.to be true }
    end

    context 'failure' do
      let!(:attributes) { attributes_for(:user, :invalid_email) }
      it { is_expected.to be false }
    end
  end
end
```

## ローカルでの確認

ローカルで RSpec（と rubocop）を実施する場合は、以下のコマンドを実行して下さい。

```
$ docker-compose run app rspec
$ docker-compose run app rubocop
```

## GitHub へ push

問題がなければ git commit と git push を行います。
これだけで GitHub へ反映されると同時に CircleCI が RSpec と rubocop を自動実行してくれます。

今回は RSpec と rubocop の実施だけですが、エラーがなかった場合にそのまま本番環境へのデプロイなどを行うことも可能です。

少し駆け足となってしまいましたが、今回は以上となります。

# 参考資料
以下の記事、情報を参考にさせていただきました。
ありがとうございます。

- [入門ガイド](https://circleci.com/docs/ja/2.0/getting-started/)
- [言語ガイド: Ruby](https://circleci.com/docs/ja/2.0/language-ruby/)
- [データベースの構成例](https://circleci.com/docs/ja/2.0/postgres-config/)
- [circleci　コンテナの立ち上げ失敗＆ビルドがキャンセルされる？](https://qiita.com/ashketcham/items/d870c035feaaac590270)