Everyday RailsでRspecについて学んだこと

## ドキュメント形式での出力
```.rspec
--format documentation
```
この設定を適用すると、実行中の各テストケースの名前が出力されるため、どのテストが成功したか、または失敗したかを一目で把握することができます。

これにより、テストの結果が直感的に理解しやすくなり、問題の特定と修正が迅速に行えるようになります。

### 設定前

設定を加える前は、テストの進捗が`.`や`F`で表示され、どのテストが失敗したのかすぐにはわかりません。

```markdown
....F.....
```

### 設定後

`--format documentation`を設定すると、各テストケースの説明が表示されるため、どのテストが成功し、どのテストが失敗したのかが一目でわかります。

```markdown
計算
  二つの数字を加算する
    2と2を渡した時
      4と等しいこと
    -1と-1を渡した時
      -2と等しいこと
  二つの数字を減算する
    2と2を渡した時
      0と等しいこと
  二つの数字を乗算する
    3と3を渡した時
      9と等しいこと
  二つの数字を除算する
    9と3を渡した時
    (FAILED - 1)
```

## ベストプラクティスに沿う
Rspecの書き方については、下記のベストプラクティスに従うと読みやすくなります。

もちろんです。以下に日本語の例を挙げます。

### 期待する結果を能動系で明示的に記述する

**不適切な例:**
```ruby
it 'メールアドレスを持って保存されるか' do
  user = User.new(email: 'test@example.com')
  expect(user.save).to be_truthy # 能動系ではなく、結果が漠然としている
end
```

**適切な例:**
```ruby
it '有効なメールアドレスを持つユーザーは保存される' do
  user = User.new(email: 'test@example.com')
  expect(user).to be_valid # 能動的で明確な期待
end
```

### テスト結果を動詞を使って説明する

**不適切な例:**
```ruby
it 'メールアドレスの存在' do
  user = User.new(email: nil)
  expect(user).not_to be_valid # 何が期待されるか不明確
end
```

**適切な例:**
```ruby
it 'メールアドレスがない場合、ユーザーは拒否される' do
  user = User.new(email: nil)
  expect(user).not_to be_valid # 明確な行動と結果
end
```

### チェックする結果をテスト一つにつき一個だけにする

**不適切な例:**
```ruby
it '有効なメールアドレスとパスワードでユーザーが作成される' do
  user = User.new(email: 'test@example.com', password: 'password')
  expect(user).to be_valid
  expect(user.email).to eq('test@example.com') # 複数のアサーション
  expect(user.password).to eq('password')
end
```

**適切な例:**
```ruby
# ユーザー作成のテスト
it '有効なメールアドレスを持つユーザーは保存される' do
  user = User.new(email: 'test@example.com', password: 'password')
  expect(user).to be_valid
end

# メールアドレスの検証
it 'ユーザーのメールアドレスが正しく設定されている' do
  user = User.create(email: 'test@example.com', password: 'password')
  expect(user.email).to eq('test@example.com')
end

# パスワードの検証
it 'ユーザーのパスワードが正しく設定されている' do
  user = User.create(email: 'test@example.com', password: 'password')
  expect(user.password).to eq('password')
end
```

## describeとcontextの使い分け
この二つにもそれぞれ書く内容には定義があります。

### describeの使い方

`describe`は主に、テストの対象となるメソッドやクラス、機能など「何をテストしているのか」をグルーピングするために使います。

```ruby
RSpec.describe User do
  describe '#name' do
    it 'ユーザーのフルネームを返す' do
      user = User.new(first_name: '太郎', last_name: '山田')
      expect(user.name).to eq '山田 太郎'
    end
  end
end
```

### contextの使い方

`context`は「どのような状況や前提条件でテストをしているのか」を説明するときに使います。`context`は`describe`の中でさらに状況を細分化して使われることが多いです。

```ruby
RSpec.describe User do
  describe '#name' do
    context '名前と姓が両方存在する場合' do
      it 'フルネームをスペースで区切って返す' do
        user = User.new(first_name: '太郎', last_name: '山田')
        expect(user.name).to eq '山田 太郎'
      end
    end
    
    context '姓のみが存在する場合' do
      it '姓のみを返す' do
        user = User.new(last_name: '山田')
        expect(user.name).to eq '山田'
      end
    end
  end
end
```

### itとspecifyの使い分け

`it`、`specify`、`example`などのエイリアスがあります。
下記が参考になります。

https://zenn.dev/noraworld/articles/rspec-rules-for-team

## コントローラのテストは時代遅れ
コントローラのテストは、最近では非推奨とされています。
代わりとしてリクエストスペックの使用が推奨されています。

### リクエストスペックを書くタイミング

リクエストスペックは基本的に外部のAPIとの通信をテストするさいに使い、基本的はモデルとシステムテストのみを書くようにします。
これはこのEveryday Railsの著者の伊藤淳一さんが答えた動画などが記載されている記事があります。

https://qiita.com/tofuonfire/items/cc45eb35704d898ee592

## システムテストにおけるドライバの適切な使い分け

システムテストを高速かつ効率的に行うには、テストの内容に応じて適切なドライバを選択することが鍵です。主に、「JavaScriptを使うか否か」によってドライバを使い分けます。

- **JavaScriptを使用しないテスト**：これらは`rack_test`ドライバで行います。`rack_test`はJavaScriptの実行をサポートしていない代わりに、非常に高速でテストを実行できます。HTMLコンテンツの生成やフォームの送信など、シンプルな操作をテストする際に適しています。

- **JavaScriptを使用するテスト**：`selenium_chrome`ドライバを使用します。これにより、JavaScriptの動作を含むユーザーインタラクションをエミュレートできますが、テストの実行速度は低下する可能性があります。

### Capybaraの設定例

以下のようにRSpecの設定を行うことで、テストのタイプに応じて自動的にドライバを切り替えることができます。

```spec/support/capybara.rb
RSpec.configure do |config|
  config.before(:each, type: :system) do
    driven_by :rack_test
  end

  config.before(:each, type: :system, js: true) do
    driven_by :selenium_chrome
  end
end
```

`js: true`タグを付けることで、JavaScriptが必要なテストケースで`selenium_chrome`を使うように指定します。

さらに、`selenium_chromeheadless`を使用すると、ブラウザウィンドウを開かずにテストを実行することができるため、テストの実行速度が向上します。テストの大部分が通過するようになったら、ヘッドレスモードへの移行を検討しましょう。

## DRYにするためにサポートモジュールを活用する

例えばログイン処理を各テストに何度も書くのはDRY原則に反しています。
メソッド化してもいいですが、よりファイル内のコードを減らすために、サポートモジュールに切り出します。

```spec/support/login_macros.rb
module LoginMacros
  def login_as(user)
    visit root_path
    find('.btn-primary.btn-lg', text: 'ログイン').click
    fill_in 'メールアドレス', with: user.email
    fill_in 'パスワード', with: 'password'
    click_button 'ログイン'
    expect(page).to have_content "ログインに成功しました"
  end
end
```
## 失敗のテストはまとめる
Rspecは通常、一つの検証が失敗するとそこで停止します。
ですが、`aggregate_failures`で囲むと一つ検証（エクスペクテーション）が失敗しても続けて検証します。

### aggregate_failuresを使わない場合

```
RSpec.describe 'Calculator' do
  it 'correctly performs arithmetic' do
    calculator = Calculator.new
    expect(calculator.add(2, 2)).to eq(4)         # 成功
    expect(calculator.subtract(5, 3)).to eq(3)    # 失敗
    expect(calculator.multiply(3, 3)).to eq(9)    # 実行されず
  end
end
```

### aggregate_failuresを使用した場合

```
RSpec.describe 'Calculator' do
  it 'correctly performs arithmetic', :aggregate_failures do
    calculator = Calculator.new
    expect(calculator.add(2, 2)).to eq(4)         # 成功
    expect(calculator.subtract(5, 3)).to eq(2)    # 失敗
    expect(calculator.multiply(3, 3)).to eq(9)    # 成功
    expect(calculator.divide(9, 3)).to eq(3)      # 成功
  end
end
```

## モデルバリデーションテストの効率化

`shoulda-matchers`は、モデルバリデーションのテストコードを簡潔に記述することを可能にするgemです。これにより、一般的なバリデーションのテストケースをわずか数行のコードで表現することができます。

### 導入方法

まず、Gemfileに以下を追加して`shoulda-matchers` gemをインストールします。

```ruby
group :test do
  gem 'shoulda-matchers'
end
```

そして、`bundle install`を実行してgemをプロジェクトにインストールします。

### 設定の追加

インストール後、RSpecに`shoulda-matchers`を認識させるための設定を追加する必要があります。`spec/rails_helper.rb`または`spec_helper.rb`に以下の設定を追加してください。

```ruby:spec/rails_helper.rb
Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end
```

この設定を行うことで、RSpecは`shoulda-matchers`を利用する準備が整います。

### 実装例の確認

実際のコードへの適用例を見ることで、`shoulda-matchers`の利便性をより深く理解することができます。下記のGitHubリンクでは、私が行った実装を通じて、実際にどのように`shoulda-matchers`がテストコードをシンプルにするかを示しています。

[GitHub Commit: shoulda-matchersの導入](https://github.com/shanbenkazuki/tier_atelier/commit/b61f3c2cfbfaa59934e15fb9419dcc16fd2b5236#diff-8a788acbcf0d54c381950a6e34af7ad4304f15d204554c101259d2d73ee21b68)

## 特定のテストだけ実行させる

`focus: true`をテストに追加して、特定のテストケースに焦点を当ててRSpecを実行したい場合は、以下のコマンドを使用します。

```shell
rspec --tag focus
```

これにより、開発中に特定の機能やバグ修正に集中したテストを簡単に行うことができます。

## アップロードしたファイルを自動的に削除

下記を設定すると自動で削除されるようになります。
```
config.after(:suite) do
  FileUtils.rm_rf(ActiveStorage::Blob.service.root)
end
```

## アドバイス

- まずはコードを書いてからテストを書いてもよい。ただし、セットで行うこと
- システムスペックから描き始められるようになること
- テストする時間を作ること
- 常にシンプルであること