はじめに
業務でRspecを使っているので備忘録を残しておきます
キーワード
Rspec, let, let!, before, before_all, subject, context
目次
- Rsepcとは
- 構文
- 環境構築
- いくつかのTips
- 例
- 参考
Rspecとは
Rspecは、Rubyのテストフレームワークの一つであり、振る舞い駆動開発(BDD)スタイルのテストをサポートしています。BDDは、ソフトウェアの振る舞いを記述することで、開発者と非開発者が共通の言語を使用してコミュニケーションを行うことを目的としています。
構文
Rspecの構文は、主に以下の要素から構成されています。
- describe:テストする対象を記述するブロック
- メソッドを書くとき ’#method’
- クラスを書くとき Class
- context:describeの中で、テストをグループ化するブロック
- it:テストケースを記述するブロック
- expect:テストの期待値を指定するメソッド
- should:expectと併用して、テストの期待値を指定する方法
betterrspecsによるとexpectを使うのがよさそうです
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
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://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
モデルをテストする例です。
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でコントローラーのテストを行う例です。
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