13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RailsにRspecをいれてモデルのテストを書いてみよう[初心者向け]

Last updated at Posted at 2019-04-16

こんにちは。
Cryptogamesで働いているじゅん(@BoNingennnN)です。

Railsでは標準でminitestが入ってきます。
しかし、弊社ではminitestを使わずRSpecを使っています。
また、個人プロジェクトでもRSpecを使っています。

今回はRailsプロジェクトにRSpecを導入する方法を説明し、そしてテストの手始めとしてモデルのテストを書いていこうと思います!

まずはRailsプロジェクトにRSpecを導入しよう

既存のプロジェクトにRspecを導入する際には、既存のtestを削除する必要があります。
まずtestディレクトリを削除しましょう。

続いてRspecのgemをいれていきます

Gemfile
group :development, :test do
  gem 'rspec-rails'
end
bundle install

もしまだ既存のプロジェクトでtest用のdbを作成していない場合はそれを作成します。
config/database.ymlにtest用の記述があることを確認したら以下のコマンドを打ちましょう

rails db:create

これでtest用のdbの作成が完了です。
続いてRspecの導入を進めます。
以下のコマンドでRspecの設定ファイルなどを生成しましょう。

rails g rspec:install

ここはお好みになりますが、testの実行結果が見やすいように以下の記述を.rspec内に書いておくといいです。

.rspec
 --format documentation

これでRspecの導入は完了しました!
早速以下のコマンドを実行してテストを実行しましょう。

bundle exec rspec

結果は、、

No examples found.
Finished in 0.00079 seconds (files took 0.25041 seconds to load)
0 examples, 0 failures

大成功です。
まだテストを書いていないので、0 expamleです。
exampleはテストの数、failuresは通らなかったテストの数を返します。
まだテストを書いていないので、もちろんexample、failures共に0です。

では続いて超初歩的なモデルのテストを書いていきましょう。

モデルのテストをRspecで書こう

ここからはモデルのテストを書きます。
ここではすでにPostモデルがあると仮定します。
(ここは各自のアプリケーションにあるモデルと置き換えて考えてください🙇‍♂️)

post.rb
class Post < ApplicationRecord
  validates :title, presence: true, length: {maximum: 20}
  validates :content, presence: true

  def content_length
    self.content.length
  end
end

こんな単純なモデルはありえないですが、入門ということで簡単なモデルにしました。

既存のモデルのテストを作るには以下のコマンドを実行します。

rails g rspec:model post

すると以下のファイルが生成されます。

spec/models/post_spec.rb
require 'rails_helper'

RSpec.describe Post, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

ここで先ほどと同様にtestを実行してみましょう。

bundle exec rspec

すると先ほどとは異なる出力が見られます。

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Post add some examples to (or delete) /Users/junya/workspace/myprojects/rails-rspec-test/spec/models/post_spec.rb
     # Not yet implemented
     # ./spec/models/post_spec.rb:4


Finished in 0.00684 seconds (files took 4.16 seconds to load)
1 example, 0 failures, 1 pending

注目して欲しいのは、exmapleが0から1に変わった部分です。
これは先ほど生成したpost_spceを生成したからです。
pendingとなっているのは、「このテストは一旦後回しにしますよー」という意味です。
生成されるspecファイルは、初期でpendingとなっています。
この部分を実際に書き換えていきましょう!

と、その前にまずモデルテストでテストすべきことを書いていきます。
基本的には、そのモデルのバリデーションのテストを書きます。
さらに、そのモデルがインスタンスメソッドを持っているのであればそのテストも書きますし、クラスメソッドを持っていればクラスメソッドのテストも書きます。また、scopeに関するテストももちろん必要です。

今回の例だと、バリデーションとインスタンスメソッドがあるので、それぞれのテストを書いていきます。

バリデーションのテストを書いてみる

早速バリデーションのテストを書いていきましょう。

post_spec.rb
require 'rails_helper'

RSpec.describe Post, type: :model do
  it "is valid with title and content" do
    post = Post.new(title: "test", content: "testcontent")
    expect(post).to be_valid
  end
end

先ほどpendingと書かれていた部分を削除し、実際にテストを書いてみました。
テストを書く際には

it "テストの内容の説明" do
  テストの処理
end

という形で書きます。
モデルテストを書く際には、まずちゃんとバリデーションが通ること(be_valid)をテストします。
ここでテストを実際に実行してみましょう。

bundle exec rspec

出力は以下のようになります。

.

Finished in 0.01384 seconds (files took 3.45 seconds to load)
1 example, 0 failures

見事に1exampleが実行され、failure(テストの失敗)は0です!
ここで一度テストをわざと失敗させてみましょう。

先ほど作成したテストの中身を少し書き換えてみます。

post_spec.rb
require 'rails_helper'

RSpec.describe Post, type: :model do
  it "is valid with title and content" do
    post = Post.new(title: "test", content: "testcontent")
    expect(post).not_to be_valid #toからnot_toに変更
  end
end

そしてテストを実行してみると、、、

F

Failures:

  1) Post is valid with title and content
     Failure/Error: expect(post).not_to be_valid
       expected #<Post id: nil, title: "test", content: "testcontent", created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/post_spec.rb:6:in `block (2 levels) in <top (required)>'

Finished in 0.0629 seconds (files took 3.57 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/post_spec.rb:4 # Post is valid with title and content

見事に1example 1failureになっています!
テストが失敗したことがわかるかと思います。
こんな感じで、もし通らないテストがあるとちゃんと表示されます。

先ほど変更した部分を元に戻したら、バリデーションテストの続きを書いていきましょう。

今回のPostモデルには3つのバリデーションがあります。
・titleが存在すること
・titleの最大文字数は20文字
・contentが存在すること

それらのバリデーションのテストを実際に書いていきます

post_spec.rb
  it "is invalid without title" do
    post = Post.new(title: nil)
    post.valid?
    expect(post.errors[:title]).to include("can't be blank")
  end

  it "is invalid withoug content" do
    post = Post.new(content: nil)
    post.valid?
    expect(post.errors[:content]).to include("can't be blank")
  end

titleとcontentのpresence: trueのバリデーションのテストをまず書きました。
もしtitleがnilなら「titleは空欄じゃダメですよ」っていうエラーがあるよね、ということをテストしています。(contentも同様)
このパターンはバリデーションのテストの際によく出てくるパターンです。

続いて文字数の部分のテストを書きます。

post_spec.rb
  it "is invalid when title is more than 20 characters" do
    post = Post.new(title: ("a" * 21))
    post.valid?
    expect(post.errors[:title]).to include("is too long (maximum is 20 characters)")
  end

21文字だとエラーが出るよね、ということをテストしています。
本来だったら20文字の場合はエラーだなくてvalidだよねというのもテストした方が良さそうですが、ずいぶん長くなってきたので割愛します。

以上がバリデーションのテストです。
バリデーションのテストは結構パターンがあるので慣れればすんなり書けます。
また、バリデーションをうっかり忘れていた、なんてことも気付けたりしますので、そういう意味でもバリデーションテストはしっかり書いておいたほうがいいです。

インスタンスメソッドのテストを書いてみる

続いてインスタンスメソッドのテストも書いてみます。
今回の例だと、

post.rb
  def content_length
    self.content.length
  end

このcontent_lengthというインスタンスメソッドのテストを書きます。
インスタンスメソッドやクラスメソッドでは、適当に入力する値をいれ、その出力が正しいかをテストします。
と言ってもよく分からないかもしれないので、実際にコードを書いていきましょう。

post_rspec.rb
  it "returns length of content" do
    post = Post.create(title: "test", content: ("a" * 100))
    expect(post.content_length).to eq(100)
  end

なんとなく見ればわかるかと思いますが、contentが100文字なpostを作成し、
post.content_lengthが100と等しいことをテストしています。
これは非常に簡単な例ですが、複雑になってきても基本はこれです。
こんな感じでどんどんインスタンスメソッドやクラスメソッドのテストも書いていきましょう!!

さいごに

本当はもっと複雑なメソッドのテスト等も書きたかったのですが、最初から難しいめのテストを書くと挫折してしまう可能性があると思ったので、今回は超簡単なものだけにしました。
(本当はただ書くのが面倒くさかっただけかもです。笑)

今回の例は初歩の初歩で、factory_botも使っていませんし、モデル以外のテストも全く出てきませんでした。
実際のプロジェクトでテストを書くとなると、学ぶべきことは他にも色々ありますが、とりあえずはこのモデルの基本的なテストを書けるようになっておけばあとは色々ググりながらどうにかなるかと思います!

もし今担当しているプロジェクトや、個人開発のプロジェクトでテストを書いていないのであれば、まずはモデルテストから書いてみてください!

13
13
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
13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?