LoginSignup
41
41

More than 5 years have passed since last update.

RSpec テストコード書き方 サンプル _φ(・・*)

Last updated at Posted at 2015-09-01

今回は、テストコードの書き方メモをまとめておこうと思います。
まだ、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

41
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
41