【Ruby on rails】テストについてまとめてみた

# はじめに
こんにちは、エンジニア2年目の嶋田です。
まずは、この記事を開いていただきありがとうございます！
今年も記事の更新をしていきたいと思います。よろしくお願いします。

プログラムの「テスト」って何をするんだ？何からしたら良いんだ？と思っていませんか。
プログラムのテストとは、コードが正しく動くことを確かめる作業のことです。
私自身、最近初めてrailsでテストコードを書いたので今回はその時の学びを含めて記事にまとめたいと思いました！

Rails 7.1.1というバージョンでのテストに焦点を当てて、
(現在の最新バージョンは[7.1.2](https://rubygems.org/gems/rails/versions/7.1.2)です。2023.11.10にリリースされています。)
テストフレームワーク「Capybara」の使い方や、テストでよく使われるメソッド、注意点などを紹介します。

少しでもお役に立てたら嬉しいです！

# 目次
- [テストの基本概念](#テストの基本概念)
- [Railsのテスト環境の設定](#Railsのテスト環境の設定)
- [単体テストの作成](#単体テストの作成)
- [統合テストとシステムテスト](#統合テストとシステムテスト)
- [テストの実行と結果の確認](#テストの実行と結果の確認)
- [良いテストコードの書き方](#良いテストコードの書き方)
- [最後に](#最後に)

# テストの基本概念
テスト駆動開発（TDD）や、単体テスト、統合テスト、システムテストの違いについて説明します。

## テスト駆動開発（TDD）

テスト駆動開発（TDD）は、コードを書く前にまずテストを書く開発手法です。このプロセスは以下のようなステップで進みます。

1. **失敗するテストの作成**
    - まず、実装したい機能に対するテストを書きます。このテストは当初は失敗します。
2. **テストをパスするための最小限のコードの書き込み**
    - 次に、テストを通過させるために必要なだけの最小限のコードを書きます。
3. **リファクタリング**
    - コードがテストに合格したら、コードの構造を改善するためにリファクタリングします。

TDDは、開発プロセス全体でコードの品質を高め、バグの発生を減らすことに役立ちます。

## 単体テスト

単体テストは、アプリケーションの最も小さい単位（通常はメソッドやクラス）をテストすることです。
単体テストの目的は、個々の部分が正しく動作することを保証することです。

例えば、ユーザーモデルのメールアドレスのバリデーションをテストする場合、以下のような単体テストが書けます。

```ruby
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  test "email should be present" do
    user = User.new(email: nil)
    assert_not user.valid?
  end
end
```

## 結合テスト
統合テストは、複数のコンポーネントやシステムの部分が連携して正しく動作することを確認するテストです。
例えば、ユーザーがサインアップするプロセス全体をテストすることができます。

```ruby
require 'test_helper'

class UserSignupTest < ActionDispatch::IntegrationTest
  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name: "Example User", email: "user@example.com", password: "password", password_confirmation: "password" } }
    end
    follow_redirect!
    assert_template 'users/show'
  end
end
```

## システムテスト
システムテストは、エンドユーザーの視点からアプリケーション全体をテストすることです。
これは、ブラウザを使ったテストや、ユーザーインターフェースのテストを含むことが多いです。
Railsでは、Capybaraを使ってシステムテストを書くことが一般的です。

```ruby
require 'application_system_test_case'

class UserNavigationTest < ApplicationSystemTestCase
  test "visiting the index" do
    visit root_url
    assert_selector "h1", text: "Welcome to My App"
  end

  test "creating a new user" do
    visit new_user_path
    fill_in "Name", with: "New User"
    fill_in "Email", with: "newuser@example.com"
    click_on "Create User"

    assert_text "User was successfully created"
    assert_selector "p", text: "Name: New User"
  end
end
```

# Railsのテスト環境の設定
`Gemfile`におけるテスト関連のGemの説明と、テスト用データベースの設定方法を紹介します。

Railsでのテストを効率的かつ正確に行うためには、適切なテスト環境の設定が不可欠です。ここでは、テストに必要なGemの設定と、テスト用データベースの設定方法について説明します。

## テスト関連のGem

### Capybara

https://github.com/teamcapybara/capybara

Capybaraは、Webアプリケーションの統合テストやシステムテストをサポートするライブラリです。
ユーザーがブラウザで行うような操作を模倣し、ページの表示内容の確認やフォームの入力、リンクのクリックなどを自動化します。Capybaraは、特にシステムテストで役立ちます。

```ruby
gem 'capybara'
```

### Selenium WebDriver

https://github.com/SeleniumHQ/selenium

Selenium WebDriverは、Webブラウザを自動操作するためのツールです。
Capybaraと組み合わせて使用され、実際のブラウザ（Chrome, Firefoxなど）を起動してテストを実行することができます。
これにより、JavaScriptを含む動的なページのテストが可能になります。

```ruby
gem 'selenium-webdriver'
```

### FactoryBot

https://github.com/thoughtbot/factory_bot_rails

FactoryBotは、テストデータを簡単にセットアップできるライブラリです。
モデルオブジェクトのファクトリーを定義し、テストで必要なデータを効率的に生成できます。
これにより、テストコードの重複を減らし、データの整合性を保ちやすくなります。

```ruby
gem 'factory_bot_rails'
```

### RSpec

https://github.com/rspec/rspec-rails

RSpecは、Ruby用の行動駆動開発（BDD）フレームワークです。
RSpecを使用すると、自然言語に近い形式でテストを記述できるため、テストコードの可読性が高まります。
特に単体テストの作成に適しており、テストの記述をより直感的に行えます。

```ruby
gem 'rspec-rails'
```

## テスト用データベースの設定
Railsのテスト環境では、実際の本番データベースとは別にテスト用データベースを用意することが一般的です。config/database.ymlファイルにテスト用データベースの設定を追加します。

例えば、SQLiteを使用する場合の設定は以下のようになります。

```yaml
test:
  adapter: sqlite3
  database: db/test.sqlite3
  pool: 5
  timeout: 5000
```

この設定により、テストを実行する際には`db/test.sqlite3`データベースが使用されます。
これにより、本番環境のデータベースを汚染することなく、テストが行えます。

Railsのテスト環境を適切に設定することで、テストの精度と効率が向上します。
CapybaraやSelenium WebDriverを使用することで、ブラウザの挙動をシミュレートし、FactoryBotやRSpecを用いることで、テストデータの管理とテストコードの可読性が向上します。また、テスト用データベースの設定により、安全にテストを実行できます。

# 単体テストの作成

Railsでの単体テストは、アプリケーションの個々の部分（モデル、コントローラ、ヘルパーなど）が期待通りに動作することを確認するプロセスです。ここでは、Railsでの単体テストの基本的な作成方法と、具体的なコード例を紹介します。

## 単体テストの一般的な流れ

1. **テスト対象の選定** : テストするべきモデルやメソッドを決定します。
2. **テストデータの準備** : FactoryBotなどを使用して、テストに必要なデータを準備します。
3. **アサーションの記述** : `assert`メソッドを使用して、期待される結果を宣言します。

## 具体的なコード例

以下は、Userモデルの単体テストの一例です。メールアドレスの存在とフォーマットのバリデーションをテストしています。

```ruby
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  test "email should be present and valid" do
    user = User.new(email: "")
    assert_not user.valid?
    user.email = "invalid_email"
    assert_not user.valid?
    user.email = "user@example.com"
    assert user.valid?
  end
end
```

## 注意点
- DRY（Don't Repeat Yourself）
    - テストのセットアップや共通の操作をsetupメソッドにまとめます。
- テストカバレッジ
    - 重要な機能に対するテストをカバーすることを確認します。


# 統合テストとシステムテスト

Railsでの統合テストとシステムテストは、アプリケーションの異なる部分の連携や、エンドユーザーの視点からのアプリケーション全体の動作を確認するために重要です。ここでは、それぞれのテストの作成方法とその違いについて解説します。

## 統合テストの作成

統合テストは、アプリケーション内の異なるコンポーネントやモジュールが連携して正しく動作することを確認します。これには、複数のモデル、ビュー、コントローラーの相互作用をテストすることが含まれます。

### 具体的なコード例

以下は、ユーザーのログインプロセスの統合テストの一例です。

```ruby
require 'test_helper'

class UserLoginTest < ActionDispatch::IntegrationTest
  test "login with valid information followed by logout" do
    get login_path
    post login_path, params: { session: { email: 'user@example.com', password: 'password' } }
    
    assert_redirected_to user_path(@user)
    follow_redirect!
    assert_template 'users/show'
    assert_select "a[href=?]", logout_path
    delete logout_path
    assert_not is_logged_in?
    assert_redirected_to root_url
  end
end
```
このテストでは、ユーザーがログインフォームにアクセスし、有効な情報でログインし、その後ログアウトする一連のプロセスをテストしています。

## システムテストの作成

システムテストでは、実際のブラウザ、またはブラウザのシミュレーションを使用して、エンドユーザーの視点からアプリケーション全体の動作をテストします。これにより、ユーザーインターフェースとアプリケーションの全体的な挙動を評価できます。

### 具体的なコード例

```ruby
require 'application_system_test_case'

class UserNavigationTest < ApplicationSystemTestCase
  test "visiting the index and creating a new user" do
    visit root_url
    assert_selector "h1", text: "Welcome to My App"

    click_on "New User"
    fill_in "Name", with: "New User"
    fill_in "Email", with: "newuser@example.com"
    click_on "Create User"

    assert_text "User was successfully created"
  end
end
```
このテストでは、ユーザーがアプリケーションのホームページにアクセスし、新しいユーザーを作成するプロセスを模倣しています。

## 統合テストとシステムテストの違い

- **統合テスト**は、アプリケーションの複数の内部コンポーネントが連携して正しく機能することを確認します。これは主にバックエンドのロジックに焦点を当てています。
- **システムテスト**は、エンドユーザーの視点からアプリケーション全体の動作をテストします。これにはユーザーインターフェース、ユーザーの操作、アプリケーションの全体的な挙動が含まれます。

## 注意点

- **エンドツーエンドの体験**
    - システムテストでは、ユーザーのエンドツーエンドの体験をシミュレートするため、JavaScriptの動作やページ遷移などを十分に考慮する必要があります。
- **複雑なセットアップ**
    - 統合テストでは、複数のコンポーネントのセットアップが必要になることが多く、テストの設計には注意が必要です。
- **実行環境**
    - システムテストでは、実際のブラウザやヘッドレスブラウザ（例：Headless Chrome）を使用するため、テスト環境の設定が重要です。

# テストの実行と結果の確認

Railsプロジェクトでテストを実行し、その結果を適切に解釈することは、アプリケーションの品質を保証する上で重要です。
このセクションでは、`rails test`コマンドの使用方法、特定のテストファイルの実行方法、およびテスト結果の読み方について説明します。

## テストの実行方法

Railsで全てのテストを実行するには、`rails test`（または`rails t`）コマンドを使用します。

```bash
$ rails test
```

特定のテストファイルだけを実行したい場合は、そのファイルのパスをコマンドに追加します。

```bash
$ rails test test/models/user_test.rb
```

また、rails test:systemコマンドを使用すると、システムテストのみを実行できます。

```bash
$ rails test:system
```

## フィクスチャとテストの影響の確認
テストの作成、実行時には、自分で作成したフィクスチャやテストが他のテストに予期せぬ影響を与えていないか注意深く確認することが重要です。特に、データベースの状態やアプリケーションの状態が他のテストに影響を与える可能性があるため、テストの独立性を保つことが必要です。
自分で作成したテストの実行のみならず全体のテストを再度実行し確認するようにしましょう。

## テスト結果の読み方

`rails test`コマンドを実行すると、テストの実行結果がコンソールに表示されます。テスト結果には以下の情報が含まれます：

- **総テスト数**　:　実行されたテストの総数。
- **アサーション数**　:　テスト内で行われたアサーション（確認）の総数。
- **エラー数**　:　テスト実行中に発生したエラーの数。
- **失敗数**　:　テストが失敗した回数。

例えば、「76 runs, 386 assertions, 0 failures, 0 errors, 0 skips」という結果が表示された場合、76件のテストが実行され、386件のアサーションが行われ、失敗やエラーはなかったことを意味します。

### 失敗したテストの解析

テストが失敗した場合、どのテストが失敗したのか、何が原因で失敗したのかが表示されます。
失敗の原因を特定し、対応するコードを修正することが重要です。

### エラーの解析

テスト中に発生したエラーは、通常、コード内のバグやテストの不備によるものです。
エラーメッセージとスタックトレースを使用して、エラーの原因を特定し修正します。

## 注意点

- **テストカバレッジ**
    - 全ての重要な機能がテストによってカバーされていることを確認します。
- **定期的なテストの実行**
    - コードの変更があるたびにテストを実行し、新しいバグの導入を未然に防ぎます。
- **テストの精度**
    - テストが実際のビジネスロジックやユーザーの行動を正確に反映していることを確認します。

## おすすめの拡張機能

Rubyのテストをさらに効率的に行うために、Visual Studio Codeの拡張機能「Ruby Test Runner」をおすすめします。この拡張機能は、テストの実行や結果の表示を簡単に行えるようにするツールです。

https://marketplace.visualstudio.com/items?itemName=MateuszDrewniak.ruby-test-runner

この拡張機能を使用することで、テストの実行や結果の確認がより迅速かつ直感的に行えます。Visual Studio Codeユーザーにとっては、Rails開発の効率を大幅に向上させることができる素晴らしいツールです。


# 良いテストコードの書き方

良いテストコードは、アプリケーションの品質を保つだけでなく、保守や機能追加を容易にします。このセクションでは、テストコードの可読性と保守性を高める方法と、`RuboCop`などのツールを使用したコードの品質管理について説明します。

## テストコードの可読性

可読性の高いテストコードは、他の開発者がコードを理解しやすくします。以下の点に注意して、可読性の高いテストを書きましょう：

- **明確な命名**
    - テストメソッドの名前は、何をテストしているのかを明確に示すべきです。
- **テストの分割**
    - 一つのテストメソッド内に多くのアサーションを詰め込まず、適切にテストを分割しましょう。
- **セットアップの明確化**
    - テストのセットアップをわかりやすくし、何がテストされているのかを明確にします。

## テストの保守性

テストは常に現在のコードベースに合わせて更新される必要があります。以下の戦略を使用して、テストの保守性を高めましょう：

- **DRY（Don't Repeat Yourself）原則の適用**
    - 共通のセットアップやクリーンアップのコードは共有して使用します。
- **フィクスチャやファクトリの使用**
    - テストデータの生成にはフィクスチャやファクトリ（例：FactoryBot）を活用します。

## コード品質管理

`RuboCop`などの静的解析ツールを使用することで、コードの品質を一貫性のある方法で維持できます。これらのツールは、コーディングスタイルのガイドラインに従っているかどうかを自動的にチェックし、改善のための提案を行います。

- **RuboCopの設定**
    - `RuboCop`は、Rubyのコーディングスタイルとベストプラクティスに従ったコードを書くために役立ちます。
    - `.rubocop.yml`ファイルをプロジェクトに追加し、プロジェクト固有のルールを設定できます。
- **定期的なコードレビュー**
    - 静的解析ツールと合わせて、定期的なコードレビューを行うことで、コードの品質をさらに向上させることができます。

## テストの実行と確認

テストは、新しい機能を追加するたび、または既存のコードを変更するたびに実行する必要があります。これにより、アプリケーションの他の部分に予期せぬ影響を与えていないことを確認できます。

- **CI/CDパイプラインの利用**
    - 継続的インテグレーション/継続的デリバリー（CI/CD）パイプラインを利用して、テストを自動化します。これにより、コードの変更ごとにテストが自動的に実行され、エラーや問題を迅速に特定できます。

# 最後に
テストについてまとめてみました。
ここまでお付き合いいただきありがとうございます。
私自身まだまだ未経験エンジニアとして勉強中なので、何か間違っている情報や足りていない知識や補足等ありましたらコメントで教えていただけるとありがたいです。