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すると次の画面になるはずですっ!!
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チュートリアルをやってみましょう。