ã¦ã¼ã¶ã¼èªè¨¼ã®ãã¹ãï¼3ï¼ -- ã¹ã¿ãã®æ´»ç¨
2013/09/02
ååã¯ãã¦ã¼ã¶ã¼èªè¨¼ã失æããã·ããªãªã«ã¤ãã¦ãã¹ããéãã¾ããã
ä»åã¯ãã¦ã¼ã¶ã¼èªè¨¼ãæåããã·ããªãªã«ã¤ãã¦ãä¸æ©ä¸æ©å®è£ ãé²ãã¦ããã¾ãã
sessions#create
ã¢ã¯ã·ã§ã³ã®ä»®å®è£
ç¾å¨ã® sessions#create
ã¢ã¯ã·ã§ã³ã®ã³ã¼ãã¯æ¬¡ã®éãã§ãï¼
def create if false # æªå®è£ else flash.alert = 'ã¦ã¼ã¶ã¼åã¾ãã¯ãã¹ã¯ã¼ããæ£ããããã¾ããã' end redirect_to :root end
æèãæ¹è¨æ°ç åºç¤Ruby on Railsã㧠Rails ããã°ã©ãã³ã°ãå¦ãã æ¹ã¯ããããã次ã®ãããªã³ã¼ããæ¸ããããªãã®ã§ã¯ãªãã§ããããã
def create if customer = Customer.authenticate(params[:username], params[:password]) session[:customer_id] = customer.id else flash.alert = 'ã¦ã¼ã¶ã¼åã¾ãã¯ãã¹ã¯ã¼ããæ£ããããã¾ããã' end redirect_to :root end
ããã¤ãã®çç±ã«ããå°æ¥çã«ãã®ã³ã¼ããå¤æ´ããã¤ããã§ããããã£ãããããæ¡ç¨ãã¾ãããã
ç¶ãã¦ãapp/views/top/index.html.erb
ã次ã®ããã«å¤æ´ãã¾ãï¼
<h1>Top#index</h1> <p>Hello World!</p> <%= render 'login_form' unless session[:customer_id] %>
ã»ãã·ã§ã³ãªãã¸ã§ã¯ãã® :customer_id
ãã¼ã«å¤ãã»ããããã¦ããªãå ´åã®ã¿ããã°ã¤ã³ãã©ã¼ã ã表示ããããã«ãã¾ããã
Customer
ã¯ã©ã¹ã® authenticate
ã¡ã½ãããæªå®è£
ã§ãããã¨ãé¤ãã°ãããã§è¯ãããã§ãã
ã§ããã¹ããå®è¡ããã¨ã次ã®ãããªçµæã¨ãªãã¾ãï¼
Failures: 1) ãã°ã¤ã³ ã¦ã¼ã¶ã¼èªè¨¼æå Failure/Error: click_button 'ãã°ã¤ã³' NoMethodError: undefined method `authenticate' for #<Class:0xb7fe3f3c> # ./app/controllers/sessions_controller.rb:3:in `create' # ./spec/features/login_and_logout_spec.rb:9:in `block (3 levels) in <top (required)>' # ./spec/features/login_and_logout_spec.rb:6:in `block (2 levels) in <top (required)>' 2) ãã°ã¤ã³ ã¦ã¼ã¶ã¼èªè¨¼å¤±æ Failure/Error: click_button 'ãã°ã¤ã³' NoMethodError: undefined method `authenticate' for #<Class:0xb7fe3f3c> # ./app/controllers/sessions_controller.rb:3:in `create' # ./spec/features/login_and_logout_spec.rb:19:in `block (3 levels) in <top (required)>' # ./spec/features/login_and_logout_spec.rb:16:in `block (2 levels) in <top (required)>'
æ£ãããauthenticate
ã¡ã½ãããæªå®ç¾©ãã¨ããã¨ã©ã¼ãåºã¦ãã¾ãã
ã¡ã½ããã¹ã¿ãããããã¯ã¹ã¿ã
ãã¦ããã®ãã¹ããéãã«ã¯ Customer.authenticate
ã¡ã½ãããå®è£
ããå¿
è¦ãããã¾ããããããæ¬ç©ã®ã¡ã½ãããå®è£
ããã«ã¯ãããã¤ãã®å·¥ç¨ãè¸ã¾ãªããã°ãªãã¾ãããã¦ã¼ã¶ã¼åããã¹ã¯ã¼ããã¤ã¸ã§ã¹ããè¨é²ããããã®ã«ã©ã ã customers
ãã¼ãã«ã«è¿½å ãããã¹ã¯ã¼ãã®ä¿åã¨ç
§åã«é¢ããã³ã¼ããå®è£
ããå¿
è¦ãããã¾ãããã¡ãããã¹ããæ¸ããªãã¡ãããã¾ãããé¡ä¼¼ã®æ©è½ãä½åº¦ãä½ã£ããã¨ãããã°çæéã§çµããããããã¾ããããæ¸ç±ã Google ã®å©ããåããã«ããããæ¸ãã人ã¯ãããªã«å¤ããªãã§ãããã
ãã¾ç§ãã¡ãç®æãã¦ãããã¨ã¯ãã¦ã¼ã¶ã¼èªè¨¼æ©è½ã®ã¦ã¼ã¶ã¼ã¤ã³ã¿ã¼ãã§ã¼ã¹ï¼è¦ããã«ãè¦ãç®ãï¼ã«é¢ãããã¹ããéããã¨ã§ããOutside-In
ã®ååã«å¾ã£ã¦ãæãå¤å´ã®é¨åãä½ã£ã¦ãã¾ããå¤å´ãæªå®æãªéã¯ããªãã¹ãå
å´ã®å®è£
ã«ç
©ããããã¹ãã§ã¯ããã¾ããããã®æ®µéã§ãã¹ã¯ã¼ãæ
å ±ã®è¨é²æ¹æ³ãç
§åæ段ã«ã¤ãã¦èãå§ããã¨ãæèã®æµããé®æããã¦ãã¾ãã¾ãã
ããã¡ã½ããã®å®è£ ãå¾åãã«ããããããããã®ã¡ã½ãããåå¨ããªãã¨ãã¹ããéããªãâ¦ããã®ã¸ã¬ã³ãããæã ãæã£ã¦ãããã®ããRSpec ã®ã¡ã½ããã¹ã¿ãã¨ããæ©è½ã§ããé常ã¯ãçãã¹ã¿ãã¨å¼ã³ã¾ãã
ã¾ãã¯ç¨ä¾ãè¦ã¦ãã ãããspec/features/login_and_logout_spec.rb
ãå°ãã ãå¤æ´ãã¾ããï¼
require 'spec_helper' describe 'ãã°ã¤ã³' do specify 'ã¦ã¼ã¶ã¼èªè¨¼æå' do Customer.stub(:authenticate) visit root_path within('form#new_session') do fill_in 'username', with: 'taro' fill_in 'password', with: 'correct_password' click_button 'ãã°ã¤ã³' end expect(page).not_to have_css('form#new_session') end specify 'ã¦ã¼ã¶ã¼èªè¨¼å¤±æ' do Customer.stub(:authenticate) visit root_path within('form#new_session') do fill_in 'username', with: 'taro' fill_in 'password', with: 'wrong_password' click_button 'ãã°ã¤ã³' end expect(page).to have_css('p.alert', text: 'ã¦ã¼ã¶ã¼åã¾ãã¯ãã¹ã¯ã¼ããæ£ããããã¾ããã') expect(page).to have_css('form#new_session') end end
5è¡ç®ã¨16è¡ç®ã« Customer.stub(:authenticate)
ã¨ããã³ã¼ãã追å ããã¦ãã¾ããããã¯ãCustomer
ã¯ã©ã¹ã« authenticate
ã¨ããã¹ã¿ããå®ç¾©ãã¾ããã¹ã¿ãï¼stub
ï¼ã¯ãæ¬ç©ã®ã¡ã½ããã®ä»£ããã«ä¸æçã«ï¼ãã¹ãã®éã ãï¼å®ç¾©ãããã¡ã½ãããæãç¨èªã§ããstub
ã¡ã½ããã®å¼æ°ãã¹ã¿ãã®ååã¨ãªãã¾ãã
è±èªã® "stub" ã®å義ã¯ãï¼æ¨¹æ¨ã®ï¼åãæ ªãã§ãä»ã«ã¯ãï¼æ¯ã®ï¼æãæ®ããï¼éçããã°ãã®ï¼ä½¿ãæ®ããï¼å ¥å ´å¸ã®ï¼åå¸ãã¨ãã£ãæå³ãããã¾ããã©ãããçµç·¯ã§ã³ã³ãã¥ã¼ã¿ç¨èªã® "stub" ãçã¾ããã®ãã¯ã調ã¹ãã¤ãã¾ããã§ããããåãã®æ¹ãããã£ãããã¾ãããããã²æãã¦ãã ããã
ã¹ã¿ããå®ç¾©ããçµæããã¹ãã®çµæã¯æ¬¡ã®ããã«å¤åãã¾ãï¼
Failures: 1) ãã°ã¤ã³ ã¦ã¼ã¶ã¼èªè¨¼æå Failure/Error: expect(page).not_to have_css('form#new_session') Capybara::ExpectationNotMet: expected not to find css "form#new_session", found 1 match: "ã¦ã¼ã¶ã¼å ãã¹ã¯ã¼ã" # ./spec/features/login_and_logout_spec.rb:12:in `block (2 levels) in <top (required)>'
確ãã« NoMethodError
ã¯åºãªããªãã¾ããã
ã¹ã¿ãã®æ»ãå¤ãæå®ãã
次ã®ã¹ãããã¯ããã¦ã¼ã¶ã¼èªè¨¼æåãã®ã·ããªãªã«ããã¦ã¯ãCustomer.authenticate
ã¡ã½ããã Customer
ã¯ã©ã¹ã®ã¤ã³ã¹ã¿ã³ã¹ãè¿ãããã«ãããã¨ã§ãã
ããã¯ã¨ã¦ãç°¡åã§ããspec/features/login_and_logout_spec.rb
ã®5è¡ç®ã次ã®ããã«ä¿®æ£ãã¦ãã ããã
Customer.stub(:authenticate).and_return(Customer.new)
and_return
ã¯ãã¹ã¿ãã®æ»ãå¤ãæå®ããã¡ã½ããã§ãã
ããããã¹ãã®çµæã¯ã©ããªãã§ããããï¼
Failures: 1) ãã°ã¤ã³ ã¦ã¼ã¶ã¼èªè¨¼æå Failure/Error: expect(page).not_to have_css('form#new_session') Capybara::ExpectationNotMet: expected not to find css "form#new_session", found 1 match: "ã¦ã¼ã¶ã¼å ãã¹ã¯ã¼ã" # ./spec/features/login_and_logout_spec.rb:12:in `block (2 levels) in <top (required)>'
ããããå¤åããªãã§ããããªãã§ããããã
FactoryGirl.create
ä½ãèµ·ãã¦ããã®ããããã°ã³ã¼ããåãè¾¼ãã§èª¿ã¹ã¦ã¿ã¾ããããapp/controllers/sessions_controller.rb
ã®3è¡ç®ã« p customer
ã追å ãã¦â¦
def create if customer = Customer.authenticate(params[:username], params[:password]) p customer session[:customer_id] = customer.id else flash.alert = 'ã¦ã¼ã¶ã¼åã¾ãã¯ãã¹ã¯ã¼ããæ£ããããã¾ããã' end redirect_to :root end
ãã¹ããå®è¡ããã¨ããã¹ãã®çµæã表示ãããåã«ã¿ã¼ããã«ã«å¤æ° customer
ã®ä¸èº«ã表示ããã¾ãã
#<Customer id: nil, family_name: nil, given_name: nil, family_name_kana: nil, given_name_kana: nil, created_at: nil, updated_at: nil>
ãããåããã¾ããããCustomer.new
ã§ä½ã£ããªãã¸ã§ã¯ãã¯ãã¼ã¿ãã¼ã¹ã«ä¿åããã¦ããªãã®ã§ id
ãã»ããããã¦ããªãã®ã§ãã
FactoryGirl ã«åç»å ´é¡ãã¾ããããspec/features/login_and_logout_spec.rb
ã®5è¡ç®ã次ã®ããã«ä¿®æ£ãã¦ãã ããã
Customer.stub(:authenticate).and_return(FactoryGirl.create(:customer))
以åãCustomer
ã¢ãã«ã®ããªãã¼ã·ã§ã³å¨ãã®ãã¹ããæ¸ããéã«ã¯ããã¼ã¿ãã¼ã¹ã«ä¿åããå¿
è¦ããªãã£ãã®ã§ãFactoryGirl.build
ã使ã£ã¦ãã¾ããããä»å㯠FactoryGirl.create
ã§ãã¼ã¿ãã¼ã¹ã«ä¿åãã¾ãã
ããã§ãã¹ããéãã¾ãã
$ bin/rspec spec/features/login_and_logout_spec.rb .. Finished in 0.16719 seconds 2 examples, 0 failures Randomized with seed 5639
éããã¨ã確èªãããããããã°ã³ã¼ããåé¤ãã¦ãã ãããããã¦ãbin/rspec
ã³ãã³ããå®è¡ãã¦ãä»ã®ãã¹ãããã¹ã¦éããã¨ã確èªãã¦ãã ããã
ã¢ãã¯
以ä¸ã®ããã« RSpec ã®ã¹ã¿ãæ©è½ãç¨ããã¨ãæèã®æµãã妨ããããªãéãå®è£ ä½æ¥ãä¸æçã«ã¹ããããã¦ç®ä¸ã®å®è£ ä½æ¥ãé²ãããã¨ãã§ãã¾ãã
RSpec ç¨èªã§ã¯ãã¹ã¿ããæã¤ãªãã¸ã§ã¯ãï¼ã¯ã©ã¹ãå«ãï¼ãã¢ãã¯ï¼mockï¼ã¨å¼ã³ã¾ããä¸è¨ã®ä¾ã§ã¯ãCustomer
ã¯ã©ã¹ãã¢ãã¯ã§ãã
è±èªã® "mock" ã¯ãã¾ãããã®ãã¨ããæå³ã§ãã"mockup" ã¾ã㯠"mock-up" ã¨ããå½¢ã§ã模åãã¨ããæå³ã§ã使ããã¾ãã
RSpec ã«ã¯ stub
ã¡ã½ãã以å¤ã«ãã¢ãã¯ãä½ãæ段ãç¨æããã¦ãã¾ããdouble
ã¡ã½ããã§ãã
ãã®ã¡ã½ããã«ã¤ãã¦ã¯å¥ã®æ©ä¼ã«ç´¹ä»ãã¾ãããdouble
ã¡ã½ããã«ãã£ã¦ä½ãããã¢ãã¯ã¯ãç´ç²ãªã¢ãã¯ï¼pure mockï¼ãã¨å¼ã°ãããã¨ã ãè¦ãã¦ããã¾ããããç¹ã«ã©ã®ã¯ã©ã¹ã®ã¤ã³ã¹ã¿ã³ã¹ã§ããã¨ãæå®ããã«ã¢ãã¯ãä½ãããã±ã¼ã¹ãããããã®æã«å©ç¨ãã¾ãã
stub
ã¡ã½ããã§ä½ãããã¢ãã¯ã¯ããç´ç²ãªã¢ãã¯ãã¨å¯¾æ¯ãã¦ãé¨åçãªã¢ãã¯ï¼partial mockï¼ãã¨å¼ã°ãã¾ããåºæ¬çã«ã¯æ¬ç©ãªã®ã ããã©ãã¹ã¿ãã ãããã¾ãããã®ï¼mockï¼ãã§ãããã¨ããæå³ã§ãã
ã³ã³ãã¥ã¼ã¿ç¨èªã®ãã¢ãã¯ãã«ã¯ãããã¹ãã¹ã¿ãããããã¹ãããã«ããããã¹ãã¹ãã¤ããããã§ã¤ã¯ãªãã¸ã§ã¯ããããããã¼ãªãã¸ã§ã¯ãããªã©ã®é¡ç¾©èªãå¤æ°ããã人ã«ãã£ã¦å¾®å¦ã«èªæ³ãç°ãªãã¾ããæãå½±é¿åãæã£ãã®ããXUnit Test Patternsãï¼2007ï¼ãæ¸ããGerard Meszarosã«ããåé¡ã§ããMartin Fowler ã¯å½¼ã®è°è«ãæ´çãã¦ãæãå æ¬çãªç¨èªã¨ãã¦ããã¹ãããã«ããæããããããããã¼ããããã§ã¤ã¯ãããã¹ã¿ããããã¹ãã¤ãããã¢ãã¯ãã«åé¡ãã¦ãã¾ãï¼TestDoubleï¼ãå½¼ãã®å®ç¾©ã«ããã°ãã¢ãã¯ã¯ç¹å¥ãªåå¨ã§ãããããªãã¸ã§ã¯ãããã¹ãã®éã«ã©ã®ãããªã¡ãã»ã¼ã¸ï¼ã¡ã½ããã³ã¼ã«ï¼ãåä¿¡ãããã調ã¹ãããã«äºåã«ä»æ§ãä¸ãããããã¹ãããã«ããã¢ãã¯ãã§ããã¢ãã¯ã¯ãã¹ããå®äºããæã«ã¡ãã»ã¼ã¸åä¿¡ã®è¨é²ã¨ä»æ§ãç §ããåãããåè´ããªããã°ä¾å¤ãæãã¾ããããããRSpec ã®ç¾å¨ã®éçºã¡ã³ãã¼ãã¡ã¯ããã¢ãã¯ãã¨ããç¨èªã Meszaros/Fowler ã®ããã¹ãããã«ãã¨åããããªå æ¬çã»ç·ç§°çãªæå³ã§ç¨ãã¦ãã¾ããããã¦ãã¡ã½ããã³ã¼ã«ã®è¨é²ããã§ãã¯ããæ©è½ããã¢ãã¯ã®å¿ é æ¡ä»¶ã§ã¯ãªãåãªãä¸ã¤ã®å´é¢ã¨ãã¦æãã¦ãã¾ãã
次åã¯
次åã®ãã¦ã¼ã¶ã¼èªè¨¼ã®ãã¹ã(4)ãããã¯ãCustomer.authenticate
ã¡ã½ããã®å®è£
ã«å
¥ãã¾ããã§ã¯ãã¾ãã