LoginSignup
38
19

More than 5 years have passed since last update.

Minitestでrailsのテストを書こう

Last updated at Posted at 2018-08-11

1 対象読者: テストって何?という人

テストってなんぞや?
ってところから本を読んで実装していったのでまとめます。
railsチュートリアルをやってもテストの概念がわからなかった人向けです。
RSpecの記事は充実しているけれど、Minitestの記事ってあんまりないのです。

テストとはシステムがちゃんと動くか確かめるコードのことです。
せっかく作ったけど、このボタン押せばこっちの画面ちゃんと出るかなーって時、
ブラウザで確かめるのもよいのですが、複雑な仕様になると
思わぬミスを発見できないこともありますよね。
というかいちいちrails sしなおすのめんどくさい!
そんなときにテストコードを書いて自動化しましょう。

2 準備しましょう

まずはちょいちょいっとテーブル用意
今回はユーザー一人につき、多数のブログを持つ1対N形式で進めていきます。
$ rails new TestBlog
$ rails g model User name:string
$ rails g model Blog user_id:integer title:string body:string
$ rails g controller users show
$ rails g controller blogs show new
$ rake db:migrate

ほいっと。あっという間にユーザーとブログのモデルとコントローラができましたっ!

ついでにroutes.rbも編集してあげてあげましょう。
root 'users#show'
resource :user
resources :blogs

ほい。rails sすると次の画面になるはずですっ!!

初期画面.png

3 データベースにテストデータをいれましょう。

ディレクトリからtest/fixturesを探しましょう。
fixturesにはダミーデータを入れていきます。
今回は一人のユーザー「シマリス」が、多数のブログを持っている設定です。

test/fixtures/users.yml

one:
  id: 1
  name: シマリス

ブログのデータも入れてみましょう。

test/fixtures/blogs.yml

one:
  id: 1
  user_id: 1
  title: おいしいくるみ料理の作り方
  body: くるみはそのままでおいしいです。

two:
  id: 2
  user_id: 1
  title: どんぐり集めの穴場
  body: 小学校あたりがおすすめです。

three:
  id: 3
  user_id: 1
  title: くるみVSどんぐり!
  body: どちらもおいしいです。

モデルのアソシエーションも忘れずに編集します。

app/models/user.rb

class User < ApplicationRecord
    has_many :blogs
end
app/models/blog.rb

class Blog < ApplicationRecord
    belongs_to :user
end

最後にディレクトリからconfig/database.ymlを開き、テスト環境と開発環境で使うDBを同じにします。

config/database.yml

development:
  <<: *default
  database: db/development.sqlite3

test:
  <<: *default
  #database: db/test.sqlite3
  database: db/development.sqlite3  #<- ここをtestからdevelopmentに変更!

この状態でrake testのコマンドを打つと、fixtureに書いたデータがデータベースに入ります。
いちいち画面のフォームを整えなくともデータを簡単に入れられるのです☆
いよいよ準備が完了です。テストを書いていきましょう。

4 初めてのテスト

ターミナル上で 「rake test」と打つと、こんなテストが走るはずです。

vagrant@vagrant-ubuntu-trusty-64:/vagrant/TestBlog$ rake test
Run options: --seed 2422

# Running:

E

Error:
UsersControllerTest#test_should_get_show:
NameError: undefined local variable or method `users_show_url' for #<UsersControllerTest:0x0000000005f93558>
    test/controllers/users_controller_test.rb:6:in `block in <class:UsersControllerTest>'

bin/rails test test/controllers/users_controller_test.rb:5

~ 略 ~

Finished in 0.282132s, 10.6333 runs/s, 0.0000 assertions/s.
3 runs, 0 assertions, 0 failures, 3 errors, 0 skips

最初に注目するのは最後の行に書いてあるこれ。
3 runs, 0 assertions, 0 failures, 3 errors, 0 skips

3つのテストが走り、3つのエラーがでましたよって意味になります。

テストなんていつ書いたの?って疑問もありますが、
実はrails g controllerしたタイミングで、
裏でrailsが勝手にそれっぽいテストを書いてくれています。
それがエラーになっているよって状態なんですね。

まずはこのエラーをつぶしていきましょう☆

テストのコントローラファイルを開くと、次のようなファイルが書かれているはずです。

test/controllers/users_controller_test.rb

  test "should get show" do
    get users_show_url
    assert_response :success
  end

「users#showページはちゃんと表示されるの?」っていうシンプルなテストです。
assertの直訳は「主張する」。
基本的な文法は assert "期待する振る舞い","実装コードのメソッド","テストデータの値"
実装コードのメソッドを使って、テストデータの値を入れてあげると、期待する振る舞いになるのか、テストをする。という文法構造ですね。

現在、エラーになっております。
理由は「users_show_url」なんて存在しないからですね。
rake routesでルートを確かめて、正しいurlを貼ってあげましょう。
「ユーザー「シマリス」のshowページに行きたい」というテストが以下のコードになります。

  test "should get show" do
    #get users_show_url
    get user_url(users(:one))
    assert_response :success
  end

それではテストしてみましょう。

vagrant@vagrant-ubuntu-trusty-64:/vagrant/TestBlog$ rake test
Run options: --seed 540

# Running:

〜 略 〜

.  

Finished in 1.256377s, 2.3878 runs/s, 0.7959 assertions/s.
3 runs, 1 assertions, 0 failures, 2 errors, 0 skips

他の赤色のエラーの他に、緑色の「.」が現れたはずです。
これがテストが通った証拠ですね。無事シマリスのページがエラーなく表示されることをしめしています。

blogのコントローラのテストも修正し、テストが全て通る状態にしておきましょう。

vagrant@vagrant-ubuntu-trusty-64:/vagrant/TestBlog$ rake test
Run options: --seed 40446

# Running:

...

Finished in 1.150978s, 2.6065 runs/s, 2.6065 assertions/s.
3 runs, 3 assertions, 0 failures, 0 errors, 0 skips

5 createの成功テストを書こう

さあ、それでは、blogのcreateやdelete機能がちゃんと動くか、テストしてみましょう。
まずは普通にblogを投稿できるように、controllerやviewを編集しましょう。

controllers/users_controller.rb

class UsersController < ApplicationController

  def show
    @user = User.find(1)
  end
end
controllers/blogs_controller.rb

class BlogsController < ApplicationController
  def show
    @blog = Blog.find(params[:id])
  end

  def new
    @blog = Blog.new
  end

  def create
    @blog = Blog.new(blog_params)
    if @blog.save
      redirect_to user_path(1)
    else
      render :new, notice: 'もう一回試してみてください'
    end
  end

  private
  def blog_params
    params.require(:blog).permit(:user_id, :title, :body)
  end
end
views/users/show.html.erb

<%= @user.name %> のブログ一覧</br>
<% @user.blogs.each do |blog| %>
    title: <%= blog.title %>
    <%= link_to "本文を読む", blog_path(blog) %></br> 
<% end %>
<%= link_to '新しく記事を書く', new_blog_url(id: @user.id) %>
views/blogs/new.html.erb


<% @blog.errors.full_messages.each do |message| %>
    <div class="form-error">
         <%= message %>
     </div>
<% end %>

<%= form_for [ @blog ]  do |f| %>    
    title: <%= f.text_field :title %>
    body:  <%= f.text_field :body %>
           <%= f.hidden_field :user_id, value: params[:id] %>
           <%= f.submit '更新' %>
<% end %>

そしてblogのコントローラテストに次のような記述を書き加えます。

test/controllers/blog_controller_test.rb

  test "ブログを作ったらblogのモデル数が1増える" do
    get new_blog_url
    assert_response :success
    assert_difference 'Blog.count', 1 do
      post blogs_path, params: { blog: { 
                              user_id: 1,
                              title: '好きな果物',
                              body: "果物はスイカが好き!!",
                            } }
    end
    follow_redirect!
    assert_response :success
  end
end

期待する振る舞いは、

1、newページに飛ぶ。
2、ブログを新しく作ったらblog数が1増える。
3、成功したらshowページにredirectする。

の3つです。

ちなみに 「post」はコントローラのcreateを呼び出す機能です。
pathはrake routes コマンドで確認しましょう。

できたらrake testしてみましょう。

vagrant@vagrant-ubuntu-trusty-64:/vagrant/TestBlog$ rake test
Run options: --seed 41181

# Running:

....

Finished in 0.883742s, 4.5262 runs/s, 6.7893 assertions/s.
4 runs, 6 assertions, 0 failures, 0 errors, 0 skips

全て通るはずです。
これで、ブログのcreate機能の品質は保証されましたね。
FailureやErrorが出てしまったら、慌てずにエラー文に従い、
テストコードや実装コードを見直してミスを探しましょう。

6 ブログ作成失敗のテストを書こう

しかし現実はいつも成功するとは限りませんよね。
validateなどであえて作成失敗させる場合もあります。
てことで、失敗するテストも書きましょう。
blogのコントローラーのテストに以下の表記を加えましょう。

test/controllers/blog_controller_test.rb

  test "タイトルが入っていなくてブログ作成に失敗する" do
    get new_blog_url
    assert_response :success
    assert_no_difference 'Blog.count' do
      post blogs_path, params: { blog: { 
                              user_id: 1,
                              title: '',
                              body: '果物はスイカが好き!!',
                            } }
    end
    assert_select "div",  "Title can't be blank"
  end

期待する振る舞いは、
1、newページに飛ぶこと。
2、タイトルが入っていないのでテスト作成に失敗すること。
3、「Title can't be blank」というエラー文が表示されること。
の3点です。
この状態でrake testするとこうなります。

Run options: --seed 34868

# Running:

...F

Failure:
BlogsControllerTest#test_タイトルが入っていなくてブログ作成に失敗する [/vagrant/TestBlog/test/controllers/blogs_controller_test.rb:32]:
"Blog.count" didn't change by 0.
Expected: 3
  Actual: 4


bin/rails test test/controllers/blogs_controller_test.rb:29

.

Finished in 0.801987s, 6.2345 runs/s, 9.9752 assertions/s.
5 runs, 8 assertions, 1 failures, 0 errors, 0 skips

失敗してしまいました。
つまり、今の実装コードでは、タイトルが空でもブログを作ってしまうため、
「タイトルが空だとブログ作成できない」という状態にならないのです。

そこで、実装コードをテストの期待する状態に近づけていきましょう。
具体的にはblogのモデルにvaridateを追加します。

app/models/blog.rb

class Blog < ApplicationRecord
    belongs_to :user
    validates :title, presence: true
end

これで、タイトルが空だったらブログ作成が失敗するようになりました。
rake testを走らせると??

vagrant@vagrant-ubuntu-trusty-64:/vagrant/TestBlog$ rake test
Run options: --seed 49302

# Running:

.....

Finished in 0.944267s, 5.2951 runs/s, 9.5312 assertions/s.
5 runs, 9 assertions, 0 failures, 0 errors, 0 skips
vagrant@vagrant-ubuntu-trusty-64:/vagrant/TestBlog$

こうですね。
全て成功しました!

もちろんテストはこれだけではありません。
他にも様々な書き方がありますので、
気になる方は railsチュートリアルをやってみましょう。

38
19
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
38
19