Rspecの備忘録


## はじめに

業務でRspecを使っているので備忘録を残しておきます

## キーワード

Rspec, let, let!, before, before_all, subject,  context

## 目次

1. Rsepcとは
2. 構文
3. 環境構築
4. いくつかのTips
5. 例
6. 参考

## Rspecとは

Rspecは、Rubyのテストフレームワークの一つであり、振る舞い駆動開発（BDD）スタイルのテストをサポートしています。BDDは、ソフトウェアの振る舞いを記述することで、開発者と非開発者が共通の言語を使用してコミュニケーションを行うことを目的としています。

## 構文

Rspecの構文は、主に以下の要素から構成されています。

- describe：テストする対象を記述するブロック
    - メソッドを書くとき ’#method’
    - クラスを書くとき Class
- context：describeの中で、テストをグループ化するブロック
- it：テストケースを記述するブロック
- expect：テストの期待値を指定するメソッド
- should：expectと併用して、テストの期待値を指定する方法

betterrspecsによるとexpectを使うのがよさそうです

[https://www.betterspecs.org/](https://www.betterspecs.org/)

```ruby
describe 'テストの対象(クラスやメソッド)' do
	context '条件など(ログイン時、等)' do 
		it '成功する' do 
		// テストを書く
		expect(テストしたい対象).to 期待する内容
		end
	end
end
```

また、RSpecでは、事前に定義されたいくつかの特殊なメソッドを使用することができます。以下にいくつかの代表的なものを紹介します。

- let：テスト用の変数を定義するメソッド
- let!：letと同じくテスト用の変数を定義するメソッドですが、呼び出し時に即座に実行されます
- before：テスト前に実行される処理を定義するメソッド
- before_all：describe内の全てのテストが実行される前に一度だけ実行される処理を定義するメソッド
- subject：テスト対象のインスタンスを返すメソッド
- pending: 実装前のテストに対して使用するメソッド
- skip: テストをスキップするメソッド

pending, skipなどは使用したことはありませんが、let, let!, beforeなどは使用する機会が多いのかと思います

インスタンス変数は使用せず、上記のメソッドを使用して変数を宣言したりテスト用レコードを作ったりします。

beforeは変数宣言以外にも使用できます。let!とbeforeはほぼ同様だと思います。beforeを使用するのは複数のテストを書くときに使いまわしたい時かと思います。

[https://qiita.com/kikikikimorimori/items/9512bdff1053ce483c0a](https://qiita.com/kikikikimorimori/items/9512bdff1053ce483c0a)

[http://www.code-magagine.com/?p=7773](http://www.code-magagine.com/?p=7773)

応用ですが、テストの規模が大きくなってくると、let_it_be、before_allなどのメソッドを使用することでテスト時間を短縮する必要があるかもしれません。

[https://github.com/test-prof/test-prof/blob/master/docs/recipes/let_it_be.md](https://github.com/test-prof/test-prof/blob/master/docs/recipes/let_it_be.md)

[https://qiita.com/WaiChan/items/feb665dafa47db1f46da](https://qiita.com/WaiChan/items/feb665dafa47db1f46da)

## 準備

Rspecを使用するためには、まずRspecをインストールする必要があります。以下のコマンドを実行することでインストールすることができます。

```
gem install rspec

```

また、Railsプロジェクトで使用する場合は、Gemfileに以下のように追記し、`bundle install`コマンドを実行してください。

```
group :development, :test do
  gem 'rspec-rails', '~> 3.0'
end

```

## いくつかのTips

- テスト名は英語で書くことが一般的です。また、テスト名は「どのような状況で」「何が起こるか」「期待される結果は何か」を表すように書くと良いです。
- FactoryBotを使用して、テスト用のデータを作成することができます。これにより、テストの繰り返し作業を減らすことができます。
- Rspecのエラーメッセージは非常にわかりやすく、どこが問題なのかをすぐに特定することができます。エラーメッセージを読み解くことで、どのような問題が発生したのかを理解し、修正することができます。
- テストコードは、コードの品質を保つためにも定期的に整理することが重要です。不要なテストコードを削除し、重複したコードを共通化することで、保守性の高いテストコードを作成することができます。

## 例1

以下は、RspecでRailsアプリケーションの`User`モデルをテストする例です。

```ruby
RSpec.describe User, type: :model do
  describe 'validations' do
    it 'is invalid without a name' do
      user = build(:user, name: nil)
      expect(user).not_to be_valid
    end

    it 'is invalid without an email' do
      user = build(:user, email: nil)
      expect(user).not_to be_valid
    end

    it 'is valid with a name and email' do
      user = build(:user)
      expect(user).to be_valid
    end
  end

  describe 'associations' do
    it { should have_many(:posts) }
  end
end

```

この例では、`User`モデルのバリデーションと関連をテストしています。`build`メソッドを使用して、`User`モデルのインスタンスを生成し、`expect`メソッドを使用して期待値を指定しています。

`create`メソッドはレコードを作りますが、`build`メソッドはインスタンスを作成するだけ、という違いがあります。

## 例2

以下は、Rspecでコントローラーのテストを行う例です。

```ruby
RSpec.describe UsersController, type: :controller do
  describe 'GET #index' do
    it 'renders the index template' do
      get :index
      expect(response).to render_template(:index)
    end

    it 'assigns @users' do
      user = create(:user)
      get :index
      expect(assigns(:users)).to eq([user])
    end
  end

  describe 'POST #create' do
    context 'with valid attributes' do
      it 'creates a new user' do
        expect {
          post :create, params: { user: attributes_for(:user) }
        }.to change(User, :count).by(1)
      end

      it 'redirects to the new user' do
        post :create, params: { user: attributes_for(:user) }
        expect(response).to redirect_to(User.last)
      end
    end

    context 'with invalid attributes' do
      it 'does not create a new user' do
        expect {
          post :create, params: { user: attributes_for(:user, name: nil) }
        }.not_to change(User, :count)
      end

      it 're-renders the new template' do
        post :create, params: { user: attributes_for(:user, name: nil) }
        expect(response).to render_template(:new)
      end
    end
```

## 参考

- [https://rspec.info/documentation/3.12/rspec-core/](https://rspec.info/documentation/3.12/rspec-core/)
- [https://www.betterspecs.org/](https://www.betterspecs.org/)
- [https://qiita.com/uchiko/items/d34c5d1298934252f58f](https://qiita.com/uchiko/items/d34c5d1298934252f58f)