DockerでRails7+Mysql8.0環境を構築してRspecを書くまで

# はじめに
Rails6のサポート期間が切れるためRails7に移行する必要があるのと、Rspecを練習できる環境を作りたかったので簡単にまとめてみます。

# 準備
## 作業フォルダと必要なファイルを作成
```
$ mkdir rails7
$ cd rails7
$ touch {docker-compose.yml,Dockerfile,Gemfile,Gemfile.lock}
```

## ディレクトリ構成
```
rails7
┣ docker-compose.yml
┣ Gemfile
┣ Gemfile.lock
┣ Dockerfile
```   

## 各ファイル内容
```docker-compose.yml
version: '3.9'
services:
  db:
    image: mysql:8.0
    platform: linux/amd64
    environment:
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - db-data:/var/lib/mysql
    ports:
      - "3306:3306"
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && rails server -b '0.0.0.0'"
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    depends_on:
      - db

volumes:
  db-data:
```

```:Dockerfile
FROM ruby:3.1.0

RUN mkdir /app
WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN bundle install
COPY . /app

EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```

```:Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '3.1.0'

gem 'rails', '~> 7.0.4'
gem 'mysql2'
```

# Railsプロジェクトを作成
今回はjavascriptの設定はしていません
```
$ docker-compose run web rails new . \
--force --no-deps --database=mysql --skip-javascript
```

## config/database.ymlを修正
```config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  host: db

development:
  <<: *default
  database: app_development

test:
  <<: *default
  database: app_test
```

コンテナを起動します
```
$ docker-compose up --build
```

Railsのコンテナでデータベースを作成します
```
$ docker exec -it コンテナ名 bash
bundle exec rails db:create
```

# Railsの動作確認
localhost:3000にアクセスして以下の画面が表示されていればOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/f7a57601-13be-9bf1-16d0-a3bf3bbdcb6e.png)

# Mysqlの動作確認
```
# コンテナに入る
$ docker exec -it rails7-db-1 sh
```

```
# passwordはpassword
$ mysql -u root -p
```
railsのコンテナ内でrails db:createを実行後、
mysqlに接続するとconfig/database.ymlで定義している通り、app_developmentやapp_testが作成されていることが確認できます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/a9d47c97-5781-aa81-80f7-c2807644524f.png)


# Rspec導入

Gemfileにrspec-railsとfactory_bot_railsを追加します

```:Gemfile
略
group :development, :test do
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
  gem "rspec-rails"
  gem "factory_bot_rails"
end
```

Gemfileを変更したのでbundle installします

```
$ docker-compose build
$ docker-compose up
```

コンテナに入ってrspecの基本設定をします
```
$ docker exec -it rails7-web-1 sh
# rails g rspec:install
```

Factroybotの設定をします
```spec/rails_helper.rb
略
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end
```

# Rspec作成
今回は以下の記事を参考に簡単なCRUDのAPIを作成し、それに対して簡単なテストのみ書きました

https://elekibear.com/20221110_01

```app/controllers/tasks_controller.rb
class TasksController < ApplicationController
    before_action :set_task, only: %i[edit update destroy]

    def index
      @tasks = Task.all
    end

    def new
      @task = Task.new
    end

    def create
      @task = Task.new(task_params)
      if @task.save
        redirect_to tasks_path
      end
    end

    def edit
    end

    def update
      if @task.update(task_params)
        redirect_to tasks_path
      end
    end

    def destroy
      @task.destroy
      redirect_to tasks_path
    end

    private
    def task_params
      params.require(:task).permit(:content)
    end

    def set_task
        @task = Task.find(params[:id])
    end
  end
```

API作成後、rspecフォルダにrequestsフォルダを作成しtask_request.rbファイルを作成します

```spec/requests/task_request.rb
require 'rails_helper'

RSpec.describe "Tasks", type: :request do
  let(:new_task) { create(:task) }
  let(:content) { "content" }
  let(:params) {{
    content: content, 
  }}
  let(:update_params) {{
    content: "updated_content", 
  }}
  let(:task_id) { new_task.id }

  describe "GET /tasks tasks#index" do
    it "アクセスできること" do
        get tasks_path
        expect(response).to have_http_status(200)
    end
  end

  describe "GET /tasks/new tasks#new" do
    it "アクセスできること" do
        get new_task_path
        expect(response).to have_http_status(200)
    end
  end

  describe "POST /tasks tasks#create" do
    it "アクセスできること" do
        post tasks_path, params: { task: params }
        expect(response).to have_http_status(302)
    end
  end

  describe "GET /tasks/:id/edit tasks#edit" do
    it "アクセスできること" do
        get edit_task_path(task_id)
        expect(response).to have_http_status(200)
    end
  end

  describe "PUT /tasks/:id tasks#update" do
    it "アクセスできること" do
        put task_path(id: task_id, task: update_params)
        expect(response).to have_http_status(302)
    end
  end

  describe "DELETE /tasks/:id tasks#destroy" do
    it "アクセスできること" do
        delete task_path(task_id)
        expect(response).to have_http_status(302)
    end
  end
end
```

Rspecを実行
```
$ bundle exec rspec spec/requests/tasks_request.rb
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/06d019c2-b240-f05c-c2cb-08a122a67d91.png)

# おわりに
Rails6のサポート期間が切れるので、Rails7について学んでいきたいです。
Rspecも書く量を増やして定着を図りたいです。


# 参考
https://elekibear.com/20221110_01

https://qiita.com/tmasuyama/items/5252925a1533adaab723