ã¦ã¼ã¶ã¼èªè¨¼ã®ãã¹ã(4) -- YAGNI
2013/09/04
ååã¯ãRSpec ã®ã¹ã¿ãæ©è½ãå©ç¨ã㦠Customer.authenticate
ã¡ã½ããã®å®è£
ãå¾åãã«ãã¤ã¤ãã¦ã¼ã¶ã¼èªè¨¼ãæåããããã·ããªãªã«ã¤ãã¦ãã¹ããéãã¾ããã
ä»åã¯ãCustomer.authenticate
ã¡ã½ããèªä½ã®å®è£
ã«çæãã¾ãã
FactoryGirl::Syntax::Methods
ã¢ã¸ã¥ã¼ã«
æ¬é¡ã«å ¥ãåã«ããã¹ãã³ã¼ãã®åéãæ¸ããæ¹æ³ãç´¹ä»ãã¦ããã¾ãã
ç¾å¨ãåæ㧠FactoryGirl.build
ããã㯠FactoryGirl.create
ã¨ããã¡ã½ããå¼ã³åºããè¨è¿°ããã¦ãã¾ããä»å¾ã使ç¨ããåæ°ã¯å¢ããã°ããã§ãã
ããã§ãspec/spec_helper.rb
ã次ã®ããã«ä¿®æ£ãã¾ãï¼
# (çç¥) RSpec.configure do |config| # (çç¥) config.expect_with :rspec do |c| c.syntax = :expect end config.include FactoryGirl::Syntax::Methods end
10è¡ç®ã§ FactoryGirl::Syntax::Methods
ã¢ã¸ã¥ã¼ã«ããã¹ãã³ã¼ãã«ã¤ã³ã¯ã«ã¼ãããè¨å®ããã¦ãã¾ãã
ãããã㨠FactoryGirl.build
ããã³ FactoryGirl.create
ã®ä»£ããã« build
ããã㯠create
ã¨æ¸ããããã«ãªãã¾ãã
ã¤ã¾ããspec/features/login_and_logout_spec.rb
ã¯
Customer.stub(:authenticate).and_return(FactoryGirl.create(:customer))
ã¨æ¸ã代ããã«ã
Customer.stub(:authenticate).and_return(create(:customer))
ã¨çãæ¸ããããã§ãã
ã¨ã°ã¶ã³ãã«ã°ã«ã¼ãã®é層å
ãã¦ãç¾å¨ã® spec/models/customer_spec.rb
ã®ã³ã¼ãã¯æ¬¡ã®ãããªæ§é ãæã£ã¦ãã¾ãã
require 'spec_helper' describe Customer do specify '...' do # ... end specify '...' do # ... end # ... end
ãã§ã«èª¬æããããã«ãspecify
ã¡ã½ããã«å°ããããããã¯ãã¨ã°ã¶ã³ãã«ã§ãããããå²ã describe
ãããã¯ãã¨ã°ã¶ã³ãã«ã°ã«ã¼ãã§ãã
ã¨ã°ã¶ã³ãã«ã°ã«ã¼ãã¯æ¬¡ã®ããã«å ¥ãåã«ãããã¨ãå¯è½ã§ãã
require 'spec_helper' describe Customer do describe 'A' do specify '...' do # ... end specify '...' do # ... end # ... end describe 'B' do specify '...' do # ... end specify '...' do # ... end # ... end end
ãããã㨠Customer
ã¯ã©ã¹ã®ä»æ§ã示ãããã®ã¨ã°ã¶ã³ãã«ããAãã¨ãBãã«åé¡ã§ãã¾ãã
ãããã¯ã次ã®ããã«ãæ¸ãã¾ãã
require 'spec_helper' describe Customer, 'A' do specify '...' do # ... end specify '...' do # ... end # ... end describe Customer, 'B' do specify '...' do # ... end specify '...' do # ... end # ... end
çè ã¯ä¸»ã«å¾è ã®æ¸ãæ¹ãæ¡ç¨ãã¦ãã¾ããã¨ã°ã¶ã³ãã«ã®æ°ãå¢ãã¦ããã¨ãåè ã®æ¸ãæ¹ã§ã¯ã¨ã°ã¶ã³ãã«ã°ã«ã¼ãã®æ§é ãææ¡ãã«ãããªãããã§ãã
ã¯ã©ã¹ã¡ã½ãã Customer.authenticate
ã®ãã¹ã
spec/models/customer_spec.rb
ã次ã®ããã«æ¸ãæãã¦ãã ããï¼
require 'spec_helper' describe Customer, 'ããªãã¼ã·ã§ã³' do # (çç¥) end describe Customer, '.authenticate' do specify 'ã¦ã¼ã¶ã¼åã¨ãã¹ã¯ã¼ãã«è©²å½ããCustomerãªãã¸ã§ã¯ããè¿ã' do # ... end end
æ°ãã«ã¯ã©ã¹ã¡ã½ãã Customer.authenticate
ã®ä»æ§ãè¨è¿°ããããã®ã¨ã°ã¶ã³ãã«ã°ã«ã¼ããä½æããæåã®ã¨ã°ã¶ã³ãã«ã®ã²ãªå½¢ãæ¸ããã¨ããã§ãã# ...
ã®é¨åã«ã¯ã©ããªãã¹ããæ¸ãã°ããã§ãããããã¡ãã£ã¨èãã¦ã¿ã¦ãã ããã
ãã§ã« sessions#create
ã¢ã¯ã·ã§ã³ã®ä¸ã§ãã®ã¯ã©ã¹ã¡ã½ããã使ç¨ããã¦ãã¾ãã
Customer.authenticate(params[:username], params[:password])
ãã®ä½¿ç¨ä¾ããã¹ããæ¸ãéã®åèã«ãªãã¾ãã
ã¾ãããããªæãã§ããããï¼
describe Customer, '.authenticate' do let(:customer) { create(:customer, username: 'taro', password: 'correct_password') } specify 'ã¦ã¼ã¶ã¼åã¨ãã¹ã¯ã¼ãã«è©²å½ããCustomerãªãã¸ã§ã¯ããè¿ã' do result = Customer.authenticate(customer.username, 'correct_password') expect(result).to eq(customer) end end
2è¡ç®ã® create
㯠FactoryGirl.create
ã¨åãã§ãã'taro'
ã¨ããã¦ã¼ã¶ã¼å㨠'correct_password'
ã¨ãããã¹ã¯ã¼ããæã¤é¡§å®¢ã®ã¢ãã«ãªãã¸ã§ã¯ããä½æããããããã«ãã¼ã¡ã½ãã customer
ã§åç
§ã§ããããã«ãã¦ãã¾ãã
5è¡ç®ã§ã¯ãã¯ã©ã¹ã¡ã½ãã Customer.authenticate
ãå®éã«å¼ãã§ããã®æ»ãå¤ããã¼ã«ã«å¤æ° result
ã«ã»ãããã¦ãã¾ããããã¦ããã¼ã«ã«å¤æ° result
ãæããªãã¸ã§ã¯ã㨠ãã«ãã¼ã¡ã½ãã customer
ãè¿ããªãã¸ã§ã¯ããåä¸ã§ãããã¨ã確èªãã¦ãã¾ãã
ãã¹ããå®è¡ãã¦ã¿ãã¨ãçµæã¯æ¬¡ã®éãï¼
Failures: 1) Customer.authenticate ã¦ã¼ã¶ã¼åã¨ãã¹ã¯ã¼ãã«è©²å½ããCustomerãªãã¸ã§ã¯ããè¿ã Failure/Error: let(:customer) { create(:customer, username: 'taro', password: 'correct_password') } NoMethodError: undefined method `username=' for #<Customer:0xb9b250fc> # ./spec/models/customer_spec.rb:68:in `block (2 levels) in <top (required)>' # ./spec/models/customer_spec.rb:71:in `block (2 levels) in <top (required)>'
username=
ã¡ã½ãããæªå®ç¾©ã ã¨æå¥ãè¨ã£ã¦ãã¾ããæ³å®éãã§ãã次ã«é²ã¿ã¾ãããã
username
ã«ã©ã ã®è¿½å
ã¨ã©ã¼ã解æ¶ãããã customers
ãã¼ãã«ã« username
ã«ã©ã ã追å ãã¾ããdb/migrate/..._create_customers.rb
ã次ã®ããã«ä¿®æ£ãã¦ãã ããï¼4è¡ç®ã追å ï¼ã
class CreateCustomers < ActiveRecord::Migration def change create_table :customers do |t| t.string :username, null: false t.string :family_name, null: false t.string :given_name, null: false t.string :family_name_kana, null: false t.string :given_name_kana, null: false t.timestamps end end end
ããã¦ããã¤ã°ã¬ã¼ã·ã§ã³ãããç´ãã¾ãã
$ rake db:migrate:reset $ rake db:test:prepare
ãã¹ãã®çµæã¯ããå¤ããã¾ãï¼
Failures: 1) Customer.authenticate ã¦ã¼ã¶ã¼åã¨ãã¹ã¯ã¼ãã«è©²å½ããCustomerãªãã¸ã§ã¯ããè¿ã Failure/Error: let(:customer) { create(:customer, username: 'taro', password: 'correct_password') } NoMethodError: undefined method `password=' for #<Customer:0xba0cb1f0> # ./spec/models/customer_spec.rb:68:in `block (2 levels) in <top (required)>' # ./spec/models/customer_spec.rb:71:in `block (2 levels) in <top (required)>'
ä»åº¦ã¯ password=
ã¡ã½ãããæªå®ç¾©ã ããã§ãã
password
å±æ§ã®å®ç¾©
å¹³æã®ãã¹ã¯ã¼ãããã¼ã¿ãã¼ã¹ã«è¨é²ããã®ã¯è¯ãç¿æ
£ã§ã¯ããã¾ããã®ã§ãä¸æçã«ãã¹ã¯ã¼ããè¨æ¶ãã¦ããããã® password
å±æ§ã Customer
ã¯ã©ã¹ã«å®ç¾©ãã¾ãããã¼ã¿ãã¼ã¹ã«ã¯ä½ããã®æ¹æ³ã§ãã¹ã¯ã¼ããå¤æããå¤ãæ ¼ç´ãããã¨ã«ãªãã¾ãããä»ã¯ãã®ãã¨ã«ã¤ãã¦èãã¾ããã
app/models/customer.rb
ã次ã®ããã«ä¿®æ£ãã¦ãã ããï¼4è¡ç®ã追å ï¼ã
require 'nkf' class Customer < ActiveRecord::Base attr_accessor :password validates :family_name, :given_name, :family_name_kana, :given_name_kana, presence: true, length: { maximum: 40 } # (çç¥) end
ããã¦ããã¹ãã®çµæï¼
Failures: 1) Customer.authenticate ã¦ã¼ã¶ã¼åã¨ãã¹ã¯ã¼ãã«è©²å½ããCustomerãªãã¸ã§ã¯ããè¿ã Failure/Error: result = Customer.authenticate(customer.username, 'correct_password') NoMethodError: undefined method `authenticate' for #<Class:0xb90a585c> # ./spec/models/customer_spec.rb:71:in `block (2 levels) in <top (required)>'
ãããããã¹ã対象ã®ã¡ã½ããã«ãã©ãçãã¾ããã
ã¯ã©ã¹ã¡ã½ãã Customer.authenticate
ã®ä»®å®è£
ããããã¯ã©ã¹ã¡ã½ãã Customer.authenticate
ã®å®è£
ã«å
¥ãã¾ãããããã§å¤§åãªã®ã¯ãã¾ãå
åããã¦èããã«ãã¨ã«ããç®ã®åã«ããã¨ã©ã¼ã解æ¶ãããã¨ã ãã«éä¸ãããã¨ã§ãã
app/models/customer.rb
ã次ã®ããã«ä¿®æ£ãã¦ãã ããï¼20-24è¡ã追å ï¼ã
require 'nkf' class Customer < ActiveRecord::Base attr_accessor :password validates :family_name, :given_name, :family_name_kana, :given_name_kana, presence: true, length: { maximum: 40 } validates :family_name, :given_name, format: { with: /\A[\p{Han}\p{Hiragana}\p{Katakana}]+\z/, allow_blank: true } validates :family_name_kana, :given_name_kana, format: { with: /\A\p{Katakana}+\z/, allow_blank: true } before_validation do self.family_name = NKF.nkf('-w', family_name) if family_name self.given_name = NKF.nkf('-w', given_name) if given_name self.family_name_kana = NKF.nkf('-wh2', family_name_kana) if family_name_kana self.given_name_kana = NKF.nkf('-wh2', given_name_kana) if given_name_kana end class << self def authenticate(username, password) find_by_username(username) end end end
æåæãããã»ã©åç´ãªå®è£ ã§ããããæ£ç¢ºã«è¨ãã°ãã¾ã£ããä¸ååãªå®è£ ã§ããã¦ã¼ã¶ã¼èªè¨¼ã«ãªã£ã¦ãã¾ãããã§ããããã§ããã®ã§ããæç´ã«ä¸æ©ä¸æ©é²ãã§ãã ããã
Factory Girl ã®ä¿®æ£
ãã¹ã spec/models/customer_spec.rb
ã¯éãã¾ãããããããspec
ãã£ã¬ã¯ããªå
¨ä½ã«ã¤ãã¦ãã¹ããå®è¡ããã¨ã次ã®ãããªå¤±æãå ±åããã¾ãã
Failures: 1) ãã°ã¤ã³ ã¦ã¼ã¶ã¼èªè¨¼æå Failure/Error: Customer.stub(:authenticate).and_return(create(:customer)) ActiveRecord::StatementInvalid: Mysql2::Error: Field 'username' doesn't have a default value: INSERT INTO `customers` (çç¥) # ./spec/features/login_and_logout_spec.rb:5:in `block (2 levels) in <top (required)>'
ãã¼ã¿ãã¼ã¹ã¹ãã¼ããå¤æ´ãããã®ã§ãFactory Girl ã®å®ç¾©ãå¤æ´ãå¿ è¦ã«ãªã£ãã®ã§ãã
spec/factories/customers.rb
ã次ã®ããã«ä¿®æ£ãã¦ãã ããï¼3è¡ç®ãæ¿å
¥ï¼ã
FactoryGirl.define do factory :customer do username 'taro' family_name 'å±±ç°' given_name '太é' family_name_kana 'ã¤ãã' given_name_kana 'ã¿ãã¦' end end
ããã§ããã¹ã¦ã®ã¨ã°ã¶ã³ãã«ãæåãã¾ãã
YAGNI -- ä½ããããªããã¨ã大äº
ãã¹ãé§åéçºã®ååãåããããã示ããããããã¹ããæ¸ãããã¹ããéãããã®æå°éã®å®è£ ãè¡ããã¨ããçããµã¤ã¯ã«ãé常ã«ä¸å¯§ã«ç¹°ãè¿ãã¾ããã
ãã®çããµã¤ã¯ã«ã®ç¹°ãè¿ãããã¹ãé§åéçºã®èã§ãã
é常ã¯ããã¾ã§ä¸å¯§ã«ãããªãã¦ãæ§ãã¾ãããããããã½ããã¦ã§ã¢éçºã§å°é£ã«ç´é¢ããæãããããä¸å¯§ãªããæ¹ãå½¹ã«ç«ã¡ã¾ããæ¥ãã°åãã§ãã
ãYAGNIãã¨ããè¨èããåãã§ããããã"You ain't gonna need it" ã®çç¥å½¢ã§ããã½ããã¦ã§ã¢éçºã§ã¯ãã¨ã§ä½¿ãã ããã¨æã£ã¦ä½ã£ããã®ã®å¤§åã¯ç¡é§ã«ãªããã¨ããçµé¨åã表ããè¨èã§ããããã¯ç¡é§ã«ãªãã°ããã§ã¯ãªããã½ã¼ã¹ã³ã¼ããä¹±éã«ããã½ããã¦ã§ã¢ã®ä¿å®æ§ãä½ä¸ããã¾ããããã¯ããªããä½ã£ããã®ã®ä¾¡å¤ãæ¸ããã®ã§ãã
ãã¹ãé§åã®çãéçºãµã¤ã¯ã«ãç¹°ãè¿ããã¨ã§ãä½ãããã®å¼å®³ãé¿ãããã¨ãã§ãã¾ãã
次åã¯
ã¯ã©ã¹ã¡ã½ãã Customer.authenticate
ã®å®è£
ã¯ã¾ã ã¾ã ç¶ãã¾ãã次åã¯ããã¹ã¯ã¼ããä¸è´ããªãå ´åã«ã¤ãã¦ã
ã§ã¯ãã¾ãã