今回は、テストコードの書き方メモをまとめておこうと思います。
まだ、RSpecのコードを書き始めたばかりなので、リファクタリングが必要な箇所があるかと思いますm(_ _)m
※設定等の準備編は下記にまとめました。
RSpecテスト環境 設定 メモ _φ(・・*) - Qiita
環境
・Rails 4.1.4
・ActiveRecord 4.1.4
・Mongoid 4.0.0
・Rspec 3.1
テストデータ FactoryGirl
※モデル名等はここにあげるにあたり、適当なのに変えていてあまり意味はありません。
FactoryGirl.define do
factory :task do
sequence(:seq) {|n| n}
good false
done false
comment "MyString"
memo "MyString"
factory :invalid_task do
good nil
end
end
end
ログインユーザーの対応
※参考:Rails+Devise+Rspecでログインが必要なControllerをテスト
#spec/rails_helper.rb
config.include Devise::TestHelpers, type: :controller
config.extend ControllerMacros, :type => :controller
#spec/support/controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryGirl.create(:user)
end
end
def login_admin
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin)
end
end
end
modelの基本的なところ
# 特殊なところ等、メソッドなど省略しています
require 'rails_helper'
RSpec.describe Task, :type => :model do
describe "FactoryGirl be valid" do
it { expect(build(:task)).to be_valid }
end
it { is_expected.to be_timestamped_document }
it { is_expected.to have_index_for(seq: 1) }
describe "have fields" do
it { is_expected.to have_fields(:good).of_type(Mongoid::Boolean) }
it { is_expected.to have_field(:done).of_type(Mongoid::Boolean).with_default_value_of(false) }
it { is_expected.to have_fields(:comment, :memo).of_type(String) }
end
describe "Association be valid" do
it { is_expected.to belong_to(:note) }
end
describe "Validation be valid" do
it { is_expected.to validate_presence_of(:good) }
end
describe "scope be valid" do
it "order_default" do
first_three_item_createds = Task
.desc(:created_at).limit(3).pluck(:created_at)
expect(Task
.order_default.limit(3).pluck(:created_at))
.to match_array first_three_item_createds
end
end
end
Controllerの基本的なところ
# 特殊なところ等、一部省略しています
require 'rails_helper'
RSpec.describe Admins::TasksController, :type => :controller do
describe "GET #index" do
login_admin
before(:each){ create_list(:task, 3)}
context "デフォルト(条件指定のパラメータがない場合)" do
before(:each){ get :index }
it "解決済み(done: true)は返さない" do
create(:task, done: true)
expect(assigns(:admins_tasks).pluck(:done))
.to_not match_array([true])
end
it "返される数は、20件以下のlimitがかかっていること" do
create_list(:task, 21)
expect(assigns(:admins_tasks).count(true) <= 20).to eq true
end
end
it "params[:note_id]で指定があれば、それを返すこと" do
note = create(:note)
task = create(:task, note_id: note.id)
get :index, note_id: note.seq
expect(assigns(:admins_tasks).count).to eq 1
end
describe "params[:search]で検索条件があれば、それを返すこと" do
it "コメント検索" do
comment = "コメント"
task = create(:task, comment: comment)
get :index, search: {comment: comment}
expect(assigns(:admins_tasks).last.comment).to eq comment
end
it "メモ検索" do
memo = "メモ"
task = create(:task, memo: memo)
get :index, search: {memo: memo}
expect(assigns(:admins_tasks).last.memo).to include memo
end
end
it "renders ther :index template" do
get :index
expect(response).to render_template :index
end
end
describe "POST #create" do
context "with valid attributes" do
it "saves the new record in the DB" do
expect{
xhr :post, :create, task: attributes_for(:task, note_id: note.id)
}.to change(Task, :count).by(1)
end
it '@task_note_seq be valid' do
xhr :post, :create, task: attributes_for(:task, note_id: note.id)
expect(assigns(:task_note_seq)).to eq note.seq
end
end
context "with invalid attributes" do
it "does not save the new record in the DB" do
expect{
xhr :post, :create, task: attributes_for(:invalid_task, note_id: note.id)
}.not_to change(Task, :count)
end
end
end
describe "PATCH #update" do
login_admin
let(:task) { create(:task) }
context "valid attributes" do
it "locates the requested" do
xhr :patch, :update, id: task, task: attributes_for(:task)
expect(assigns(:task)).to eq(task)
end
it "changes attributes :done" do
xhr :patch, :update, id: task,
task: attributes_for(:task, done: true)
task.reload
expect(task.done).to eq true
end
it "changes attributes :memo" do
memo = "メモ"
xhr :patch, :update, id: task,
task: attributes_for(:task, memo: memo)
task.reload
expect(task.memo).to include memo
end
end
context "with invalid attributes" do
it "does not change" do
xhr :patch, :update, id: task,
task: attributes_for(:task, done: "test")
task.reload
expect(task.done).not_to eq "test"
end
end
end
end
まだまだとりあえず書いてみたというレベルなので、
これから、もっと慣れていきたいと思います。
追記: rails g
でcontrollerのrspecテンプレも作られる
configでg.test_frameworkの設定をきちんとかいたからか、
気がついたら、新しいprojectで最初から、きちんと設定かいていったら、
rails generate でできるrspecのテンプレが素敵だったので、追記。
初心者に優しい☆
config/application.rb
config.generators do |g|
g.test_framework :rspec,
fixtures: false,
view_specs: false,
helper_specs: false,
routing_specs: false,
controller_specs: true,
request_specs: false
g.fixture_replacement :factory_girl, dir: "spec/factories"
end
$ rails g scaffold_controller Articles
spec/controllers/articles_controller_spec.rb
require 'rails_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of articles you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
RSpec.describe ArticlesController, :type => :controller do
# This should return the minimal set of attributes required to create a valid
# Article. As you add validations to Article, be sure to
# adjust the attributes here as well.
let(:valid_attributes) {
skip("Add a hash of attributes valid for your model")
}
let(:invalid_attributes) {
skip("Add a hash of attributes invalid for your model")
}
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# ArticlesController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET index" do
it "assigns all articles as @articles" do
article = Article.create! valid_attributes
get :index, {}, valid_session
expect(assigns(:articles)).to eq([article])
end
end
describe "GET show" do
it "assigns the requested article as @article" do
article = Article.create! valid_attributes
get :show, {:id => article.to_param}, valid_session
expect(assigns(:article)).to eq(article)
end
end
describe "GET new" do
it "assigns a new article as @article" do
get :new, {}, valid_session
expect(assigns(:article)).to be_a_new(Article)
end
end
describe "GET edit" do
it "assigns the requested article as @article" do
article = Article.create! valid_attributes
get :edit, {:id => article.to_param}, valid_session
expect(assigns(:article)).to eq(article)
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new Article" do
expect {
post :create, {:article => valid_attributes}, valid_session
}.to change(Article, :count).by(1)
end
it "assigns a newly created article as @article" do
post :create, {:article => valid_attributes}, valid_session
expect(assigns(:article)).to be_a(Article)
expect(assigns(:article)).to be_persisted
end
it "redirects to the created article" do
post :create, {:article => valid_attributes}, valid_session
expect(response).to redirect_to(Article.last)
end
end
describe "with invalid params" do
it "assigns a newly created but unsaved article as @article" do
post :create, {:article => invalid_attributes}, valid_session
expect(assigns(:article)).to be_a_new(Article)
end
it "re-renders the 'new' template" do
post :create, {:article => invalid_attributes}, valid_session
expect(response).to render_template("new")
end
end
end
describe "PUT update" do
describe "with valid params" do
let(:new_attributes) {
skip("Add a hash of attributes valid for your model")
}
it "updates the requested article" do
article = Article.create! valid_attributes
put :update, {:id => article.to_param, :article => new_attributes}, valid_session
article.reload
skip("Add assertions for updated state")
end
it "assigns the requested article as @article" do
article = Article.create! valid_attributes
put :update, {:id => article.to_param, :article => valid_attributes}, valid_session
expect(assigns(:article)).to eq(article)
end
it "redirects to the article" do
article = Article.create! valid_attributes
put :update, {:id => article.to_param, :article => valid_attributes}, valid_session
expect(response).to redirect_to(article)
end
end
describe "with invalid params" do
it "assigns the article as @article" do
article = Article.create! valid_attributes
put :update, {:id => article.to_param, :article => invalid_attributes}, valid_session
expect(assigns(:article)).to eq(article)
end
it "re-renders the 'edit' template" do
article = Article.create! valid_attributes
put :update, {:id => article.to_param, :article => invalid_attributes}, valid_session
expect(response).to render_template("edit")
end
end
end
describe "DELETE destroy" do
it "destroys the requested article" do
article = Article.create! valid_attributes
expect {
delete :destroy, {:id => article.to_param}, valid_session
}.to change(Article, :count).by(-1)
end
it "redirects to the articles list" do
article = Article.create! valid_attributes
delete :destroy, {:id => article.to_param}, valid_session
expect(response).to redirect_to(articles_url)
end
end
end
参考リンク
本
rspec 解説本(電子書籍)
https://leanpub.com/everydayrailsrspec-jp/read
└→本のsample > everydayrails/rspec_rails_4
https://github.com/everydayrails/rspec_rails_4
gem 解説
rspec-rails
https://github.com/rspec/rspec-rails
mongoid-rspec/mongoid-rspec
github https://github.com/mongoid-rspec/mongoid-rspec
記事
Better Specs (良い例/悪い例まとめ)
http://betterspecs.org/jp/
Factory Girl Railsのチートシート - Rails Webook
http://ruby-rails.hatenadiary.com/entry/20150102/1420174315
RailsでCucumberとRSpecを使ってテストを行う例 - Rails Webook
http://ruby-rails.hatenadiary.com/entry/20140719/1405777918
Ruby - RSpecのテンプレート - Qiita
http://qiita.com/kbaba1001/items/d01fb2b499d3122ea45e