テストから始める
静的なページとちょっと動的なページをやってそれにともなうテストってことのようです。
- テストから始める
テストはいつどのように行うものなのか?
レガシーなエンジニアはまず一通りのコーディングをして単体テストをして結合テストをする流れなわけで。
去年か一昨年か?どこかの現場で机上の空論でコーディングをする前にテストケースを書かされた。それはあくまでも概要把握ってこともあったのだと思うのだけど。
なのでテストについては考え方から変えなきゃです。
テストのメリット
テストが揃っていれば、機能停止に陥るような回帰バグ (Regression Bug: 以前のバグが再発したり機能の追加/変更に副作用が生じたりすること) を防止できる。
テストが揃っていれば、コードを安全にリファクタリング (機能を変更せずにコードを改善すること) ができる。
テストコードは、アプリケーションコードから見ればクライアントとして動作するので、アプリケーションの設計やシステムの他の部分とのインターフェイスを決めるときにも役に立つ。
なるほどねぇ〜。
最初のテスト
rails generate controller
実行時にテストファイルがちゃんと作成されている。
StaticPagesコントローラのデフォルトのテスト
test/controllers/static_pages_controller_test.rb
equire 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest test "should get home" do get static_pages_home_url assert_response :success end test "should get help" do get static_pages_help_url assert_response :success end end
このままパスすることを確認する。
$ rails test 2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
テスト駆動開発のサイクル
- 失敗するテストを最初に書く:RED
- 次にアプリケーションのコードを書いて成功させる (パスさせる):GREEN
- 必要ならリファクタリングする:REFACOR
Aboutページのテスト
test/controllers/static_pages_controller_test.rb
require 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest test "should get home" do get static_pages_home_url assert_response :success end test "should get help" do get static_pages_help_url assert_response :success end # ↓追加 test "should get about" do get static_pages_about_url assert_response :success end # ↑ end
実行
$ rails test NameError: undefined local variable or method `static_pages_about_url'
「AboutページへのURLが見つからない」ってことで、ルーティングファイルを修正する。
about用のルートを追加する
config/routes.rb
Rails.application.routes.draw do get 'static_pages/home' get 'static_pages/help' get 'static_pages/about' #←追加 root 'application#hello' end
実行
$ rails test AbstractController::ActionNotFound: The action 'about' could not be found for StaticPagesController
StaticPagesコントローラにaboutアクションがない」ってことで、コントローラファイルを修正する。
aboutアクションが追加されたStaticPagesコントローラ
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController def home end def help end # ↓ 追加 def about end # ↑ end
実行
$ rails test ActionController::UnknownFormat: StaticPagesController#about is missing a template for this request format and variant.
今度はテンプレートがないということで
$ touch app/views/static_pages/about.html.erb
touchコマンドは本来、ファイルやディレクトリのタイムスタンプだけを更新するためのコマンドなのですが、ファイルが存在しない場合には空ファイルを作成するという一種の副作用がある。クラウドIDEの場合は、ファイルツリーの更新をしないと表示されない。
Aboutページのコード
app/views/static_pages/about.html.erb
<h1>About</h1> <p> <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> is a <a href="https://railstutorial.jp/#ebook">book</a> and <a href="https://railstutorial.jp/#screencast">screencast</a> to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p>
実行
$ rails test 3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
ローカルサーバーを起動して確認。
static_pages/about
Refactor
リファクタリングっていつから一般用語になったんだろう?
コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。また、いくつかのリファクタリング手法の総称としても使われる。ただし、十分に確立された技術とはいえず、また「リファクタリング」という言葉に厳密な定義があるわけではない。
by wikipedia
ってことらしい。
去年の春になるのか?な会社はほんと必要だったな。
コードの腐敗臭というか化石化というか・・・
少しだけ動的なページ
StaticPagesコントローラのタイトルをテストする
test/controllers/static_pages_controller_test.rb
require 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest test "should get home" do get static_pages_home_url assert_response :success assert_select "title", "Home | Ruby on Rails Tutorial Sample App" end test "should get help" do get static_pages_help_url assert_response :success assert_select "title", "Help | Ruby on Rails Tutorial Sample App" end test "should get about" do get static_pages_about_url assert_response :success assert_select "title", "About | Ruby on Rails Tutorial Sample App" end end
まだ各ページにタイトルを入れてないのでテストスイートはREDになる。
タイトルを追加する(GREEN)
完全なHTML構造を備えたHomeページのビュー
app/views/static_pages/home.html.erb
<!DOCTYPE html> <html> <head> <title>Home | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>Sample App</h1> <p> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
完全なHTML構造を備えたHelpページのビュー
<!DOCTYPE html> <html> <head> <title>Help | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="https://railstutorial.jp/help">Rails Tutorial help page</a>. To get help on this sample app, see the <a href="https://railstutorial.jp/#ebook"> <em>Ruby on Rails Tutorial</em> book</a>. </p> </body> </html>
完全なHTML構造を備えたAboutページのビュー
app/views/static_pages/about.html.erb
<!DOCTYPE html> <html> <head> <title>About | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>About</h1> <p> <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> is a <a href="https://railstutorial.jp/#ebook">book</a> and <a href="https://railstutorial.jp/#screencast">screencast</a> to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p> </body> </html>
ここまでやってテストスイート。
$ rails test 3 tests, 6 assertions, 0 failures, 0 errors, 0 skips
基本タイトルを使ったStaticPagesコントローラのテスト
test/controllers/static_pages_controller_test.rb
require 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest def setup @base_title = "Ruby on Rails Tutorial Sample App" end test "should get home" do get static_pages_home_url assert_response :success assert_select "title", "Home | #{@base_title}" end test "should get help" do get static_pages_help_url assert_response :success assert_select "title", "Help | #{@base_title}" end test "should get about" do get static_pages_about_url assert_response :success assert_select "title", "About | #{@base_title}" end end
レイアウトと埋め込みRuby (Refactor)
同じコードを繰り返すことはRubyの「DRY」(Don’t Repeat Yourself: 繰り返すべからず) という原則に反する。
ほぉ〜そんな言葉があるのですか。
重複を取り除くテクニックの1つとして、ビューで「埋め込みRuby」(Embedded Ruby) が使える。
タイトルにERBを使ったHomeページのビュー
app/views/static_pages/home.html.erb
<% provide(:title, "Home") %> <!DOCTYPE html> <html> <head> <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>Sample App</h1> <p> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
タイトルにERBを使ったHelpページのビュー
app/views/static_pages/help.html.erb
<% provide(:title, "Help") %> <!DOCTYPE html> <html> <head> <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="https://railstutorial.jp/help">Rails Tutorial help section</a>. To get help on this sample app, see the <a href="https://railstutorial.jp/#ebook"> <em>Ruby on Rails Tutorial</em> book</a>. </p> </body> </html>
タイトルにERBを使ったAboutページのビュー
app/views/static_pages/about.html.erb
<% provide(:title, "About") %> <!DOCTYPE html> <html> <head> <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>About</h1> <p> <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> is a <a href="https://railstutorial.jp/#ebook">book</a> and <a href="https://railstutorial.jp/#screencast">screencast</a> to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p> </body> </html>
サンプルアプリケーションのレイアウト
もっとDRYにする。 サンプルアプリケーションのレイアウト
<!DOCTYPE html> <html> <head> <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> <%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= yield %> </body> </html>
HTML構造を削除したHomeページ
app/views/static_pages/home.html.erb
<% provide(:title, "Home") %> <h1>Sample App</h1> <p> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </p>
HTML構造を削除したHelpページ
app/views/static_pages/help.html.erb
<% provide(:title, "Help") %> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="https://railstutorial.jp/help">Rails Tutorial help section</a>. To get help on this sample app, see the <a href="https://railstutorial.jp/#ebook"><em>Ruby on Rails Tutorial</em> book</a>. </p>
HTML構造を削除したAboutページ
app/views/static_pages/about.html.erb
<% provide(:title, "About") %> <h1>About</h1> <p> <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> is a <a href="https://railstutorial.jp/#ebook">book</a> and <a href="https://railstutorial.jp/#screencast">screencast</a> to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p>
ルーティングの設定
HomeページをルートURLに設定する
config/routes.rb
Rails.application.routes.draw do root 'static_pages#home' get 'static_pages/home' get 'static_pages/help' get 'static_pages/about' end
コミット・masterブランチマージ・デプロイ
$ git add -A $ git commit -m "Finish static pages"
$ git checkout master $ git merge static-pages
$ git push
$ rails test $ git push heroku
デプロイの前にテストをするようにする。
minitest reporters
red や green を表示できるようにする
test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' require "minitest/reporters" Minitest::Reporters.use! class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.yml for all tests # in alphabetical order. fixtures :all # Add more helper methods to be used by all tests here... end
Guardによるテストの自動化
Guardは、ファイルシステムの変更を監視し、例えばstatic_pages_test.rbファイルなどを変更すると自動的にテストを実行してくれるツールです。具体的には、「home.html.erbファイルが変更されたらstatic_pages_controller_test.rbを自動的に実行する」といったことをGuardで設定することができます。
初期化をする。
$ bundle exec guard init
カスタマイズしたGuardfile
# Guardのマッチング規則を定義 guard :minitest, spring: "bin/rails test", all_on_start: false do watch(%r{^test/(.*)/?(.*)_test\.rb$}) watch('test/test_helper.rb') { 'test' } watch('config/routes.rb') { integration_tests } watch(%r{^app/models/(.*?)\.rb$}) do |matches| "test/models/#{matches[1]}_test.rb" end watch(%r{^app/controllers/(.*?)_controller\.rb$}) do |matches| resource_tests(matches[1]) end watch(%r{^app/views/([^/]*?)/.*\.html\.erb$}) do |matches| ["test/controllers/#{matches[1]}_controller_test.rb"] + integration_tests(matches[1]) end watch(%r{^app/helpers/(.*?)_helper\.rb$}) do |matches| integration_tests(matches[1]) end watch('app/views/layouts/application.html.erb') do 'test/integration/site_layout_test.rb' end watch('app/helpers/sessions_helper.rb') do integration_tests << 'test/helpers/sessions_helper_test.rb' end watch('app/controllers/sessions_controller.rb') do ['test/controllers/sessions_controller_test.rb', 'test/integration/users_login_test.rb'] end watch('app/controllers/account_activations_controller.rb') do 'test/integration/users_signup_test.rb' end watch(%r{app/views/users/*}) do resource_tests('users') + ['test/integration/microposts_interface_test.rb'] end end # 与えられたリソースに対応する統合テストを返す def integration_tests(resource = :all) if resource == :all Dir["test/integration/*"] else Dir["test/integration/#{resource}_*.rb"] end end # 与えられたリソースに対応するコントローラのテストを返す def controller_test(resource) "test/controllers/#{resource}_controller_test.rb" end # 与えられたリソースに対応するすべてのテストを返す def resource_tests(resource) integration_tests(resource) << controller_test(resource) end
隠しファイルを表示
- ファイルナビゲーターのギアアイコンをクリック
- Show hidden filesをクリック
- ルートディレクトリにある.gitignoreファイルを表示
.gitignoreにSpringを追加する
# Ignore Spring files. /spring/*.pid
を追加する。
Guardの設定が完了したら新しいターミナルで次のコマンドを実行。
$ bundle exec guard
最後にコミットしておく。
$ git add -A $ git commit -m "Complete advanced setup"