動機づけ
Railsアプリケーションを作ってみよう。せっかくなのでTest Drivenで。
とりあえず,ここから(A Guide to Testing Rails Applications)読みはじめてみる。訳は適当で,その場その場で気になったことをメモしている程度
1 Why Write Tests for your Rails Applications?
なぜTestを書くのか。
- Railsはテストを書くのを楽にしてくれるよ。ModelやControllerを作成時に,テストコードのスケルトンも一緒に作ってくれるので,そこからスタートできる。
- Railsのテスト実行は簡単。リファクタリングしても機能を満たしていることを確認できる。
- ブラウザのリクエストをシミュレートできるので,ブラウザ使わずにテストできる(ここ重要。)。
2 Introduction to Testing
"To write efficient tests, you’ll need to understand how to set up this database and populate it with sample data."2.1 The Three Environments
config/database.yml をみると,
- production
- development
- test
「rake db:test:prepare で,テスト用データベース環境の構築ができる」。
2.2 Rails Sets up for Testing from the Word Go
test フォルダの下にできるfolder
- unit
- tests for your models
- functional
- tests for your controllers
- integration
- test that invole any anumber of controllers interacting
- fixtures
- a way of organzizing test date
2.3 The Low-Down on Fixtures
Fixures: a fancy word for sample data.
Here’s a sample YAML fixture file:
ERb allosw you embed ruby code within templates. <% %>タグはRuby codeと認識される。# low & behold! I am a YAML comment! david: name: David Heinemeier Hansson birthday: 1979-10-15 profession: Systems development steve: name: Steve Ross Kellock birthday: 1974-09-27 profession: guy with keyboard
Fixtures in Action
デフォルトでは text/fixtures 以下にある全てのfixturesを
- unit test/functional test時に 読みこむ。
- Remove any exsiting data from the table corresponding to the fixture
- Load the fixture data into the table
- Dump the fixures data into a variable in case you want to access it directly
Fixtures は 基本的にはハッシュ objectなので,このようなアクセスの方法があるよ。
Fixturesからオリジナルのクラスに変換することができるので,そのクラスに許されているメソッドを実行できる。# this will return the Hash for the fixture named david users(:david) # this will return the property for david called id users(:david).id
# using the find method, we grab the "real" david as a User david = users(:david).find # and now we have access to methods only available to a User class email(david.girlfriend.email, david.location_tonight)
3. Unit Testing your Models
まず Test 用の Rails プロジェクト (TestingRails) を作成しておく。
/home/abekatsu/webroot% rails TestingRails
create
create app/controllers
create app/helpers
create app/models
create app/views/layouts
create config/environments
create config/initializers
create config/locales
create db
create doc
create lib
create lib/tasks
create log
create public/images
create public/javascripts
create public/stylesheets
create script/performance
create test/fixtures
create test/functional
create test/integration
create test/performance
create test/unit
create vendor
create vendor/plugins
create tmp/sessions
create tmp/sockets
create tmp/cache
create tmp/pids
create Rakefile
create README
create app/controllers/application_controller.rb
create app/helpers/application_helper.rb
create config/database.yml
create config/routes.rb
create config/locales/en.yml
create config/initializers/backtrace_silencers.rb
create config/initializers/inflections.rb
create config/initializers/mime_types.rb
create config/initializers/new_rails_defaults.rb
create config/initializers/session_store.rb
create config/environment.rb
create config/boot.rb
create config/environments/production.rb
create config/environments/development.rb
create config/environments/test.rb
create script/about
create script/console
create script/dbconsole
create script/destroy
create script/generate
create script/runner
create script/server
create script/plugin
create script/performance/benchmarker
create script/performance/profiler
create test/test_helper.rb
create test/performance/browsing_test.rb
create public/404.html
create public/422.html
create public/500.html
create public/index.html
create public/favicon.ico
create public/robots.txt
create public/images/rails.png
create public/javascripts/prototype.js
create public/javascripts/effects.js
create public/javascripts/dragdrop.js
create public/javascripts/controls.js
create public/javascripts/application.js
create doc/README_FOR_APP
create log/server.log
create log/production.log
create log/development.log
create log/test.log
Railsにおいて,Unit Testとはモデル(Model)のテストを書くことである。まずは,モデルpostをscaffoldで構築してみて,その際にできるtest suiteについて見てみる。
/home/abekatsu/webroot/TestingRails% script/generate scaffold post test:string body:text
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/posts
exists app/views/layouts/
exists test/functional/
exists test/unit/
create test/unit/helpers/
exists public/stylesheets/
create app/views/posts/index.html.erb
create app/views/posts/show.html.erb
create app/views/posts/new.html.erb
create app/views/posts/edit.html.erb
create app/views/layouts/posts.html.erb
create public/stylesheets/scaffold.css
create app/controllers/posts_controller.rb
create test/functional/posts_controller_test.rb
create app/helpers/posts_helper.rb
create test/unit/helpers/posts_helper_test.rb
route map.resources :posts
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/post.rb
create test/unit/post_test.rb
create test/fixtures/posts.yml
create db/migrate
create db/migrate/20090929002133_create_posts.rb
モデルに関するテスト箇所は,
create app/models/post.rb
create test/unit/post_test.rb
create test/fixtures/posts.yml
test/unit/post_test.rbを見てみる。
require 'test_helper'
class PostTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end
次の
test "the truth" do
assert true
end
がガイドと違うところ。ActiveSupport::TestCaseを見てもよくわからない。ひとまず,気にしないでおいておく。テスト用クラスは ActiveSupport::TestCase の 子クラス。テスト内容は
assert true
のように,trueがtrueであるかないか。だから,かならず成功しなければいけないテストが含んでいる。次節に進んでみる。
3.1 Preparing your Application for Testing
まずDBの構造を最新にしておくために "rake db:migrate" を実行する。
/home/abekatsu/webroot/TestingRails% rake db:migrate
(in /home/abekatsu/webroot/TestingRails)
== CreatePosts: migrating ====================================================
-- create_table(:posts)
-> 0.0019s
== CreatePosts: migrated (0.0021s) ===========================================
"db/scheme.rb" ができていることを確認して,"rake db:test:load" で,テスト用データベースを作成する。いつの時点でも,"rake db:test:load" でテスト用データベースを再構築することができる。この段階で,"rake db:test:prepare" を実行できる。
/home/abekatsu/webroot/TestingRails% rake db:test:load
(in /home/abekatsu/webroot/TestingRails)
/home/abekatsu/webroot/TestingRails% rake db:test:prepare
(in /home/abekatsu/webroot/TestingRails)
テスト用にアプリケーションを準備するためのRake Tasksは表になって示されている。 実際にテストを実行してみる。例では "ruby unit/post_test.rb -n test_truth" と test method を指定しているが,上記ではメソッド "test_truth" を定義していないので "ruby unit/post_test.rb" を実行してみる。
/home/abekatsu/webroot/TestingRails% cd test
/home/abekatsu/webroot/TestingRails/test% ruby unit/post_test.rb
Loaded suite unit/post_test
Started
.
Finished in 0.202105 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
/home/abekatsu/webroot/TestingRails/test%
次に,"タイトルがないポストを保存してはならないテスト(test_should_not_save_post_without_title)" を作成する。"unit/post_test.rb" に次のテストを追加する。
test "post should not save without title" do
post = Post.new
assert !post.save
end
今のところ,モデルPostはいじっていないので,このテストは必ず失敗するはず。実行してみる。
/home/abekatsu/webroot/TestingRails/test% ruby unit/post_test.rb
Loaded suite unit/post_test
Started
F.
Finished in 0.21082 seconds.
1) Failure:
test_post_should_not_save_without_title(PostTest) [unit/post_test.rb:11]:
is not true.
2 tests, 2 assertions, 1 failures, 0 errors
test_post_should_not_save_without_title(PostTest) [unit/post_test.rb:11] が失敗していることを確認した。もうちょっとかっこよくtestを表示させてみたいので,
test "post should not save without title" do
post = Post.new
assert !post.save, "Saved the post without a title"
end
とassert文に追加して,テスト実行。ちょっとかっこいい。
/home/abekatsu/webroot/TestingRails/test% ruby unit/post_test.rb
Loaded suite unit/post_test
Started
F.
Finished in 0.262242 seconds.
1) Failure:
test_post_should_not_save_without_title(PostTest) [unit/post_test.rb:11]:
Saved the post without a title. <== "Saved the post without a title" が表示されている。
is not true.
2 tests, 2 assertions, 1 failures, 0 errors
/home/abekatsu/webroot/TestingRails/test%
それでは,このテストが通るように,モデルPostを編集してみる。編集するファイルは "app/models/post.rb" "app/models/post.rb"を次のように編集する。titleが存在していないと保存できないようにするために "validates_presence_of :title" を追加する。
class Post < ActiveRecord::Base
validates_presence_of :title
end
"validates_presence_of" は ActiveRecord::Validations::ClassMethodsで定義されているメソッド。引数はsymbol。ここでは "title"。なぜ ActiveRecord::Validations::ClassMethods をここで呼ぶことができるのかがわからない。再びテストを実行してみる。
/home/abekatsu/webroot/TestingRails/test% ruby unit/post_test.rb
Loaded suite unit/post_test
Started
..
Finished in 0.189441 seconds.
2 tests, 2 assertions, 0 failures, 0 errors
今度はテストが成功したことがわかる。To see how an error gets reported, here's a test containing an error(うまく日本語に訳せない。)再び "post_test.rb" に戻って,
test "test sholud report error" do
some_undefined_variable
assert true
end
を追加して,テストを実行してみる。
/home/abekatsu/webroot/TestingRails/test% ruby unit/post_test.rb
Loaded suite unit/post_test
Started
.E.
Finished in 0.191917 seconds.
1) Error:
test_test_sholud_report_error(PostTest):
NameError: undefined local variable or method `some_undefined_variable' for #
unit/post_test.rb:15:in `test_test_sholud_report_error'
3 tests, 2 assertions, 0 failures, 1 errors
ここまでで 3.2 節。次節はUnit Test
わからないこと
- なぜ validates_of が ActiveRecord::Base 内で呼ぶことができるのか?
- "the truth" と書かれたところだけテストを実行してみたいのだが,どのように指定しればいいのかよくわからない。全てのテストはパスさせなければいけないから,そんなことを考えるのは無意味だろうか。
1 comment:
"%GEM_PATH%/ruby/1.8/gems/activerecord-2.3.3/lib/active_record.rb" を読むと,ActiveRecords::Validations モジュールを読むように設定されている。( autoload :Valid
ations, 'active_record/validations')。
そこで ("%GEM_PATH%/ruby/1.8/gems/activerecord-2.3.3/lib/active_record/validations.rb"),module ActiveRecord::Validations::ClassMethodsが定義されているので,validates_presense_of を呼ぶことができる。
これで納得がいった。
Post a Comment