使用Cucumber+Rspec玩转BDD(2)——邮件激活
生活随笔
收集整理的這篇文章主要介紹了
使用Cucumber+Rspec玩转BDD(2)——邮件激活
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
使用Cucumber+Rspec玩轉(zhuǎn)BDD(2)——郵件激活
2009年3月2日 星期一
### 溫故知新 ###
??? 前面我們已經(jīng)完成了新用戶注冊功能的開發(fā),為了方便我們后面的開發(fā)工作且不擾亂之前的工作成果,我們先將這份源代碼歸檔并做個標記。
??? 為了獲得更好的閱讀體驗,讀者朋友們可以在這里下載源碼:http://github.com/404/bdd_user_demo/tree/master
### 提交工作成果到GIT倉庫 ###
??? $ cd ~/code/user_demo
??? $ git init
??? $ git add .
??? $ git commit -m?"A user can be able to sign up."
??? $ git tag v1
??? “git init” 會在 ~/code/user_demo 目錄中初始化版本庫;接著 “git add .” 將 user_demo 目錄中的所有文件信息編入索引(index files);然后 “git commit” 命令將根據(jù) index 中的信息將工作內(nèi)容提交到項目的GIT倉庫里邊去,-m 選項加上了本次提交的一些說明;最后 “git tag” 給這次提交所生成的版本號標記了一個別名叫 v1。
??? 其實好習(xí)慣是在新建rails-app后就初始化版本庫。由于篇幅的關(guān)系,筆者才將些許GIT的內(nèi)容放到這篇文章中。這不,正好派上用場嘍!
??? 在主干(master)上工作是危險的,因為控制的不夠好會擾亂版本,這不是我們愿意看到的。為此,GIT允許我們在主干道的基礎(chǔ)上建立新的分支(branch),在分支中進行開發(fā)工作,這樣好控制風(fēng)險。比如有時候分支中的工作搞得一塌糊涂,開發(fā)人員想重來的時候,直接丟掉刪除這個分支再新建一個工作分支重新工作就是了,這對項目中的主干完全沒有絲毫影響(不會擾亂你上次提交到master中的工作成果),等你在新分支中開發(fā)完畢后,再將這個分支中的工作成果歸并到主干中就行。GIT的分支告訴我們,丟掉一個爛攤子比收拾一個爛攤子要輕松得多;潛意識里,我們幾乎一致認為這對開發(fā)人員的大腦是友好的!:)
??? 下面我們在主干的基礎(chǔ)上為后面郵件激活這個功能的開發(fā)新建一個分支。
### 新建工作分支 ###
????$ git checkout -b email_activation
??? 或者,
????$ git branch email_activation
????$ git checkout email_activation
??? 查看當(dāng)前工作所在的分支,
????$ git branch
??? 會返回項目中的所有分支,前面加星*就是當(dāng)前的工作分支。
??? 做開發(fā)要步步為營,不是嗎?git可以很方便地幫我們做到這點。在歸檔源碼后,接著我們新建了一個名為 email_activation 的分支,并將當(dāng)前的工作狀態(tài)從master主干切換到email_activation分支中。這里說明下,此時的 email_activation 相當(dāng)于之前源碼(v1)的一份副本,這份副本是我們進行后續(xù)開發(fā)的基礎(chǔ);后面我們將在用戶已經(jīng)能夠注冊的基礎(chǔ)上進行用戶激活帳號的開發(fā)工作,只不過在這個基礎(chǔ)上所開發(fā)的一舉一動都會被記錄到email_activation分支中。當(dāng)用戶注冊成功并能通過郵件激活帳號后,我們就可以將email_activation分支下的工作成果提交且歸并到master主干中,從而把郵件激活的功能和用戶注冊的功能完美的銜接在一起,同時使得項目的版本干凈整潔。
??? 如果我們在email_activation的分支中的開發(fā)工作不盡人意,怎么辦呢?如果是一些小小的修改,那非常好辦,直接改成你想要的就是了;可如果是大范圍地修改后,結(jié)果卻不是你想要的,有時會萌發(fā)重做的想法。下面就來告訴你一些開倒車的技巧:
??? 如果新增了文件,需要先用git add添加(這會被編入git的index,但不會提交到git倉庫),否則回滾后會遺留下來(這句話好像就是說,等到你重新開發(fā)的時候發(fā)現(xiàn)要編碼的文件已經(jīng)存在了)。可以用 git status 命令查看都添加或者修改了哪些文件。
??? 如果你當(dāng)前的工作目錄(working tree)已經(jīng)混亂不堪,但是還沒有提交,可以使用:
????$ git reset --hard
??? 這會丟棄所有的改變,包括去除已經(jīng)加到git index里邊的內(nèi)容;然后將 working tree 和 index 恢復(fù)到上次commit時的狀態(tài)。
??? 如果想回滾到一個指定的版本,就需要指定版本號:
????$ git reset --hard v1
??? v1 是我們在之前標記過的別名,即上次commit所生產(chǎn)的版本號別名,也可以替換成commit后的版本號,比如 af2d45c... ,版本號是一個唯一的哈希值,每次commit都會生成一個,省去了你找不到版本號的尷尬;基本上,使用git log 命令都能看到版本號。指定版本號的時候不需要寫上所有字符,取前5個就可以,反正能說明版本號是唯一的就行了。比如你只有兩次提交記錄,指定版本號的時候取哈希值的前兩個字符又何嘗不可呢?
??? 還有,記得 --hard 選項要慎重使用,具體的您可以使用 “git reset -h” 命令查閱更多關(guān)于撤銷修改的詳細信息。
??? 如果只是想放棄對某一文件的修改,可以使用 checkout 命令。這個命令不單用于分支間的切換,還可以回滾一個指定的文件內(nèi)容到上次所做的修改,例如:
????$ git checkout app/models/user.rb
??? 這會放棄對user.rb所做的修改,并將user.rb的內(nèi)容從上一個已提交的版本中更新回來。當(dāng)然還可以指定回滾到指定版本,例如:
????$ git checkout v1 app/models/user.rb
??? 這會將user.rb的內(nèi)容從已提交的v1所對應(yīng)的版本中更新回來。
??? 好了,到此您已經(jīng)了解了一些實用的GIT知識;是時候步入正題進行我們的開發(fā)工作了,我們來了解下工作內(nèi)容。
###?郵件激活功能?###
??? 1. 用戶成功注冊成為網(wǎng)站用戶;
??? 2. 系統(tǒng)發(fā)送一封包含激活鏈接的郵件到用戶注冊時填寫的郵箱中;
??? 3. 用戶點擊郵箱中的激活鏈來接激活帳號;
??? 4. 用戶帳號激活成功,并給出帳號激活成功的提示消息。
??? 根據(jù)上面的功能需求,我們在前面兩個故事的基礎(chǔ)上再添兩筆。
### 故事用例之用戶通過郵件激活帳號 ###
????$ gedit features/user_signup.feature
??? 修改后的文件內(nèi)容如下,
??? 功能: 注冊成為網(wǎng)站會員
????? 為了能夠瀏覽網(wǎng)站只對在線會員可見的那些內(nèi)容
????? 作為一名訪客
????? 我希望注冊成為網(wǎng)站會員
????? 場景: 用戶填寫無效數(shù)據(jù)并注冊
??????? 當(dāng) 我來到用戶注冊頁面
??????? 而且 我在輸入框<用戶名>中輸入<invalid username>
??????? 而且 我在輸入框<電子郵箱>中輸入<invalid email>
??????? 而且 我在輸入框<密碼>中輸入<password>
??????? 而且 我在輸入框<確認密碼>中輸入<verify password>
??????? 而且 我按下<注冊>按鈕
??????? 那么 我應(yīng)該看到<注冊失敗>的提示信息
????????
????? 場景: 用戶填寫正確的數(shù)據(jù)并注冊
??????? 當(dāng) 我來到用戶注冊頁面
??????? 而且 我在輸入框<用戶名>中輸入<404>
??????? 而且 我在輸入框<電子郵箱>中輸入<xuliicom@gmail.com>
??????? 而且 我在輸入框<密碼>中輸入<password>
??????? 而且 我在輸入框<確認密碼>中輸入<password>
??????? 而且 我按下<注冊>按鈕
??????? 那么 我應(yīng)該看到<注冊成功>的提示信息
??????? 而且 應(yīng)該有封激活帳號的郵件發(fā)送至<xuliicom@gmail.com>
????? 場景: 用戶激活帳號
??????? 假如 我已經(jīng)使用<404/xuliicom@gmail.com/password>注冊過
??????? 當(dāng) 我訪問<xuliicom@gmail.com>郵件中激活帳號的鏈接
??????? 那么 我應(yīng)該看到<帳號激活成功>的提示信息
??? 我們只是在已有的故事上加了個別子句。為了能讓故事跑起來,我們還需要針對故事場景中的情節(jié)編寫相應(yīng)的測試代碼。
### 編寫用于驅(qū)動故事運行的測試代碼 ###
????$ gedit features/step_definitions/user_steps.rb
??? 添加如下代碼,
??? Then /^應(yīng)該有封激活帳號的郵件發(fā)送至<(.+)>$/ do |email|
????? user = User.find_by_email(email)
????? user.activation_token.should_not be_blank
????? sent = ActionMailer::Base.deliveries.last
????? sent.to.should eql([user.email])
????? sent.subject.should =~ /激活/
????? sent.body.should =~ /#{user.activation_token}/
??? end
??? Given /^我已經(jīng)使用<(.*)\/(.*)\/(.*)>注冊過$/ do |username, email, password|
????? @valid_attributes = {
??????? :username????????????? => username,?
??????? :email???????????????? => email,?
??????? :password????????????? => password,?
??????? :password_confirmation => password
????? }
????? @user = User.create!(@valid_attributes)
??? end
??? When /^我訪問<(.*)>郵件中激活帳號的鏈接$/ do |email|
????? user = User.find_by_email(email)
????? visit activate_url(:token => user.activation_token)
??? end
??? 故事用例基本上涵蓋了我們開發(fā)的用意,測試代碼準備就緒,還等什么,趕緊跑起來看看吖。
??? 運行測試,
????$ ruby script/cucumber -l zh-CN features/user_signup.feature
????
??? 測試未能通過,原本應(yīng)該有封激活帳號的郵件發(fā)送至<xuliicom@gmail.com>,然而卻沒有,因為我們還沒有編寫用于發(fā)送激活郵件的代碼。習(xí)慣了玩測試的話,測試結(jié)果無疑對指導(dǎo)你的編碼工作非常有幫助!
??? 接下來,我們就來做這些工作。
### 添加激活碼字段 ###
??? 怎么知道用戶有沒有激活帳號呢?答案是在 users 表中增加用于標識用戶帳號是否激活的兩個字段,一個用來存放激活碼,另一個用來記錄帳號激活時間。假設(shè)這兩個字段分別是 activation_token 和 activated_at,如果 users.activation_token 字段有值,那么就說明用戶還沒有激活,如果 users.activation_token 為空且 users.activated_at 有值,那么就說明用戶已經(jīng)激活過了。
??? 下面來添加這組字段,
????$ ruby script/generate migration EmailConfirm
????$ gedit db/migrate/*_email_confirm.rb
??? class EmailConfirm < ActiveRecord::Migration
????? def self.up
??????? add_column :users, :activation_token, :string
??????? add_column :users, :activated_at, :datetime
????? end
????? def self.down
??????? remove_column :users, :activated_at
??????? remove_column :users, :activation_token
????? end
??? end
????$ rake db:migrate
????$ rake db:test:prepare
??? 表結(jié)構(gòu)準備完畢后,再來生成用戶注冊時的激活碼。
### 生成激活碼——activation_token ###
????$ gedit app/models/user.rb
??? before_create :initialize_salt, :encrypt_password, :initialize_activation_token
??? # 生成并返回標識碼
??? def generate_token
????? encrypt(Time.now.to_s.split(//).sort_by {rand}.join)
??? end
??? # 生成激活碼
??? def initialize_activation_token
????? if new_record?
??????? self.activation_token = generate_token
????? end
??? end
??? 數(shù)據(jù)模型搞定后,再從路由下手,需要指定控制器該如何分配響應(yīng)請求。
### 配置激活帳號的路由——activate_url ###
????$ gedit config/routes.rb
??? 修改后routes.rb文件內(nèi)容如下,
??? ActionController::Routing::Routes.draw do |map|
????? map.with_options :controller => 'users' do |page|
??????? page.signup '/signup', :action => 'new'
??????? page.activate '/activate/:token', :action => 'activate'
????? end
????? map.resources :users
??? end
??? 此時,如果你不清楚接下來要做什么;不妨運行測試,測試結(jié)果會告訴你答案。由于筆者知道會失敗也知曉接下里該做什么,所以就略過此步;因為Model和Route都準備完畢,是時候動手編寫業(yè)務(wù)流程了。
??? 如果你用Rails發(fā)過郵件,下面的步驟你一定很熟悉。
### 生成郵件 ###
????$ ruby script/generate mailer UserMailer confirm
????$ gedit app/models/user_mailer.rb
??? class UserMailer < ActionMailer::Base
??????
????? def confirm(user, sent_at = Time.now)
??????? subject??? '請激活您的帳號'
??????? recipients user.email
??????? from?????? 'Admin'
??????? sent_on??? sent_at
????????
??????? body?????? :username => user.username,?
?????????????????? :url? => activate_url(:token => user.activation_token)
????? end
??? end
????$ gedit app/views/user_mailer/confirm.erb
??? 親愛的 <%=@username%>:
??????? 您的帳號已經(jīng)創(chuàng)建成功,請點擊下面的鏈接激活您的帳號:
??????? <%=link_to @url, @url%>
### 發(fā)送郵件 ###
??? 用戶注冊成功之后,需要發(fā)送一封確認郵件到用戶注冊時填寫的電子郵箱中。雖然可以在 User 模型中添加 after_create 的一個回調(diào)代碼來執(zhí)行,但這樣就給 User 模型類增添了本不應(yīng)該承擔(dān)的責(zé)任;我們只需要 User 模型提供數(shù)據(jù),而不是將發(fā)送郵件的任務(wù)丟給它。這時候 ActiveRecord 提供的 Observer 就可以派上用場了,使用Observer的好處是它可以將自身連接到模型類中并注冊為回調(diào),卻無需修改任務(wù)模型類的代碼,我們將其稱之為觀察器(是否聯(lián)想到Ruby設(shè)計模式中的觀察者模式,呵呵)。下面,我們針對 User 模型創(chuàng)建一個觀察器:
????$ ruby script/generate observer User
????$ gedit app/models/user_observer.rb
??? class UserObserver < ActiveRecord::Observer
????? def after_create(user)
??????? UserMailer.deliver_confirm(user)
????? end
??? end
??? 然后在 config/environment.rb 注冊這個 Observer。
????$ gedit config/environment.rb
????config.active_record.observers = :user_observer
??? 再次發(fā)動測試引擎,看看是否working,
????$ ruby script/cucumber -l zh-CN features/user_signup.feature
????
??? 由于在生成郵件那一章節(jié)里,激活鏈接我們用的是 link_url 這種形式,如果你知道 link_url 和 link_path 的區(qū)別,那么根據(jù)上面的測試結(jié)果,你應(yīng)該了解出錯的原因。如果不了解,筆者在這里補充下,link_url 會在鏈接中加上協(xié)議名、主機名和端口號這些;而 link_path 則不用,它會直接用根目錄“/”代替之;也就是說, link_url 會在鏈接中加上網(wǎng)址;又或者說,link_url 采用絕對路徑,而 link_path 采用相對路徑。
??? 考慮到現(xiàn)實中的用戶注冊,系統(tǒng)會發(fā)送一封包含網(wǎng)址的郵件到注冊用戶的郵箱中,我們之前的郵件模板里不得不采用 link_url 這種形式。結(jié)合測試結(jié)果來看,也許此時您已經(jīng)意識到,我們是不是忘了配置主機名呢?
??? 恭喜您!您確實猜對了。
### 配置郵件中激活鏈接的絕對路徑 ###
????$ gedit app/models/user_mailer.rb
????default_url_options[:host] = HOST
??? 在 config/environments/test.rb 和 config/environments/development.rb 這兩個配置文件中定義 HOST 常量,為了開發(fā)和測試需要,這里設(shè)置成localhost就可以了。
????HOST = 'localhost:3000'
??? 不過在 config/environments/production.rb 中,HOST 常量的值就必須是真實的主機名了。
??? 另一種方法無需修改app/models/user_mailer.rb和定義HOST常量,直接在各environment/各文件或environment.rb中配置就行了,如下代碼
????$ gedit config/environment.rb
????config.action_mailer.default_url_options = { :host => 'localhost:3000' }
??? 這樣做的好處是只需修改一處。
??? 好了,補上這個配置,再運行測試,看看有什么不同。
????$ ruby script/cucumber -l zh-CN features/user_signup.feature
????
### 激活帳號 ###
??? 看來我們的郵件能夠成功發(fā)送了,不過好像訪問郵件中的確認鏈接時出了點問題,根據(jù)調(diào)試信息“ActionController::UnknownAction”顯示,應(yīng)該是沒有找到激活帳號的具體行為(action)。在前面的開發(fā)中,我們真的就還沒有編寫響應(yīng)用戶激活帳號的相關(guān)代碼,想必此時我們都清楚該做哪些工作了。
??? 我們需要給 UserController 類添加一個 Action 來響應(yīng)用戶激活帳號的請求。
????$ gedit app/controllers/users_controller.rb
??? 之前我們在config/routes.rb文件中定義了activate_path,且該activate_path 的 :action 參數(shù)指向 activate 方法;于是乎,activate 就是我們需要在 UserController 類中添加的 action。activate方法的代碼如下:
??? def activate
????? if @user = User.find_by_activation_token(params[:token])
??????? if !@user.activated?
????????? @user.email_confirm!
????????? flash.now[:notice] = '恭喜您,帳號激活成功!'
??????? end
????? end
??? end
??? 仔細觀察 UserController#activate,我們還需要在 User 模型中編寫 activated? 和 email_confirm! 這兩個實例方法,前者用來確認用戶的帳號是否已經(jīng)激活過,后者則用來激活用戶的帳號。
????$ gedit app/models/user.rb
??? 在 protected 之前添加如下兩個方法:
??? # 檢查是否已經(jīng)激活
??? def activated?
????? # 當(dāng) activation_token 為 nil 時表示用戶帳號已經(jīng)激活
????? activation_token.nil?
??? end
??????
??? # 激活帳號
??? def email_confirm!
????? update_attributes(:activation_token => nil, :activated_at => Time.now)
??? end
??? 運行測試看看,
????$ ruby script/cucumber -l zh-CN features/user_signup.feature
????
??? 看來是沒有找到模板文件,在此補上用戶成功激活帳號的頁面。
????$ gedit app/views/users/activate.html.erb
??? 保存即可。運行測試:
????$ ruby script/cucumber -l zh-CN features/user_signup.feature
??? OK,測試通過!如圖,
????
### 親臨現(xiàn)場 ###
??? 最后開發(fā)人員自己別忘了手工測試,以確保萬無一失。
??? 先清除數(shù)據(jù)庫中的記錄,
????$ ruby script/console
????>> User.delete_all
??? 假設(shè)我們以404為用戶名成功注冊后,我們來看看數(shù)據(jù)庫中404的activation_token字段是否有值。
????>> User.find_by_username('404', :select => "username, activation_token, activated_at")
????
??? 可以看到,activation_token 的值是一串加密后的字符,activated_at值為空,這說明程序已經(jīng)給注冊用戶生成了激活碼,而且此時用戶還沒有激活帳號。
??? 當(dāng)我們注冊成功后,打開郵箱卻并沒有看到激活帳號的郵件,這是怎么回事呢?
??? 因為測試程序跑到是test環(huán)境,而我們手工測試的時候,程序是運行在development環(huán)境下的,我們沒有針對development環(huán)境配置郵件服務(wù)器。下面我們采用SMTP的發(fā)信方式,這里的SMTP SERVER用的是GMAIL,而且是SSL驗證登錄方式;三次握手,發(fā)信速度沒sendmail那么快,呵呵!
????$ gedit config/environment.rb
??? config.action_mailer.delivery_method = :smtp
??? config.action_mailer.default_charset = 'utf-8'
??? config.action_mailer.smtp_settings = {
????? :address????????????? => 'smtp.gmail.com',
????? :port???????????????? => 25,
????? :domain?????????????? => 'YOUR_DOMAIN',
????? :user_name??????????? => 'YOUR_GMAL_USERNAME',
????? :password???????????? => 'YOUR_GMAIL_PASSWORD',
????? :authentication?????? => 'login',
????? :enable_starttls_auto => true
??? }
??? 該配置中大寫部分自行替換即可。
??? 清空users表,我們重新注冊404這個用戶。
????$ ruby script/console
????>> User.delete_all
??? 然后去郵箱看看,
????
??? 這回我們打開郵箱看到了激活帳號的郵件信息,不過郵件內(nèi)容中的鏈接標簽沒有生效,我們期望發(fā)送到用戶郵箱的是HTML格式的郵件。ActionMailer可以讓我們發(fā)送多種格式的郵件,只需要按相應(yīng)的內(nèi)容類型修改郵件模板的文件名格式即可。基本上,郵件模板的文件名的格式像這樣:name[.content.type].renderer;content.type 可選,缺省情況下為文本格式,你也可以手工指定為 text.plain,要發(fā)送HTML格式的郵件就需要指定為 text.html;文件后綴 renderer 一般情況下都是 erb(如果你用了HAML插件,模板后綴名應(yīng)該是haml)。下面我們將之前文本格式的郵件模板修改為網(wǎng)頁形式的:
????$ mv app/views/user_mailer/confirm.erb app/views/user_mailer/confirm.text.html.erb
??? 再次清空users表,重新注冊404這個用戶,然后前往郵箱看看我們收到的郵件是否是網(wǎng)頁格式的。
????
??? 我們看到激活帳號的超鏈接生效了(沒有將超鏈接標簽明文顯示),這說明系統(tǒng)發(fā)送出去的確實是HTML郵件。
??? 接下來我們點擊郵件中的鏈接來到了激活帳號的頁面,我們看到帳號激活成功的提示信息。
????
??? 如果激活成功,數(shù)據(jù)庫中的activation_token字段應(yīng)該是空值,且activated_at字段的值應(yīng)該為一時間戳;在之前的程序中,我們確實是按此邏輯編碼的。雖然測試成功,而且我們也非常順利地親歷了一遍注冊流程,那么是否就說明我們的應(yīng)用程序沒有程序上的漏洞了嗎?我們真的激活帳號了嗎?我們不妨看看數(shù)據(jù)庫這只黑匣子,此時應(yīng)該是讓數(shù)據(jù)說話的時候了。
????$ ruby script/console
????>> User.find_by_username('404', :select => "username, activation_token, activated_at")
????
??? 哎呀!記錄居然沒被更新,看來我們被表面現(xiàn)象給忽悠了。我想你此時也和我一樣迷惑,為什么數(shù)據(jù)記錄沒有被更新呢?這中間到底發(fā)生了什么?這讓我不由自主地想象起來,也許Rails的ORM真的修改了User實例對象的activation_token和activated_at屬性的值,只不過還沒有成功地寫入到數(shù)據(jù)庫里邊而已。果真如此嗎?如何證明這一說法成立呢?我們來看看User模型類的 email_confirm! 方法,下面是email_confirm! 方法的源碼:
??? # 激活帳號
??? def email_confirm!
????? update_attributes(:activation_token => nil, :activated_at => Time.now)
??? end
??? 我們知道,update_attributes 方法還有一個和自己長得差不多一樣的方法,即 update_attributes!
;后者比前者僅僅多一個感嘆號而已,兩者都是更新當(dāng)前模型對象所指向的數(shù)據(jù)記錄,只不過前者更新失敗會返回false,后者更新失敗則會拋出異常信息并停止程序運行,我們不妨用 update_attributes! 替換 email_confirm!方法中的update_attributes,如果問題真的出現(xiàn)在這里,至少我們也可以看見拋出的錯誤信息,這些錯誤調(diào)試信息對開發(fā)人員來說是那么的重要。
????$ gedit app/models/user.rb
??? 修改 email_confirm! 方法如下:
??? def email_confirm!
????? update_attributes!(:activation_token => nil, :activated_at => Time.now)
??? end
??? 保存,然后重新訪問或刷新激活帳號的頁面,我們看到系統(tǒng)捕獲到了非常實用的情報,如圖:
????
??? 看來問題還真的出在User模型類的email_confirm!方法這里,當(dāng)程序嘗試更新 activation_token 和 activated_at 這兩個字段時,系統(tǒng)告訴我們密碼不能為空并就此打住,程序拋出錯誤并停止執(zhí)行,后面當(dāng)然不會更新數(shù)據(jù)庫里邊的記錄了。找到出錯的原因后,我們馬上就明白 update_attributes(或update_attributes!) 會更新當(dāng)前對象所指向的記錄的所有字段,并在更新之前執(zhí)行數(shù)據(jù)校驗,如果校驗失敗就會打斷程序的運行。想到此,針對問題的解決方案也初現(xiàn)輪廓,只要程序更新指定的字段,并在更新這些指定字段的時候不去校驗其他字段的數(shù)據(jù)有效性就行了。OK,我們有非常適合用于email_confirm!的替代寫法,不妨修改email_confirm!方法如下:
????$ gedit app/models/user.rb
??? # 激活帳號
??? def email_confirm!
????? self.activation_token = nil
????? self.activated_at = Time.now
????? save(false)
??? end
??? 上述代碼中的 save 方法會更新這些字段的值,第一個參數(shù)的值指明為false后將不會執(zhí)行數(shù)據(jù)校驗,看來這一切和我們的想法非常吻合,不妨保存user.rb再刷新幾次瀏覽器看看。第一次刷新和我們初次訪問激活鏈接看到的效果一樣,都是提示帳號激活成功,后面幾次就看不到激活成功的消息了,因為帳號只需要激活成功一次就足夠了,效果確實很理想,我們?nèi)ピL問下數(shù)據(jù)庫讓它給我們做個見證。
????$ ruby script/console
????>> User.find_by_username('404', :select => "username, activation_token, activated_at")
????
??? 哈哈,數(shù)據(jù)記錄已經(jīng)更新了,這意味著程序已經(jīng)可以按照我們之前的意愿運行了。用戶提交注冊資料后會收到一封關(guān)于激活帳號的郵件,然后點擊其中的鏈接可以成功激活他的帳號。
??? 至此,我們在 email_activation 分支上的開發(fā)工作已經(jīng)順利完成,可以將工作成果歸并到主干中去了。
### 提交工作成果到GIT倉庫 ###
??? $ git add .
??? $ git commit -m?"People can activation their accounts by the confirm emails."
??? $ git checkout master
??? $ git merge email_activation
??? $ git branch -d email_activation
??? $ git tag v2
??? (注意,真正的開發(fā)中可不是到功能開發(fā)完畢了才commit,而是邊開發(fā)邊add和commit。為了方便演示編碼過程,文章中沒有一一列舉。)
### 小結(jié) ###
??? 在這篇教程中,我們的開發(fā)工作遇到了不小的挫折,尤其是在人工測試那里,經(jīng)過我們自己動手測試后,才知曉我們的程序漏洞百出。之所以這樣,是由于筆者有意而為之,其實筆者的用意非常簡單,就是想告訴開發(fā)者親臨現(xiàn)場做人工測試的重要性。也許確實讓您受挫了,覺得好像是為了測試而測試似的;大可不必有如此想法,如果您是位Rails熟手,想必也不會犯那些低級錯誤,比如update_attributes和save(false)這種區(qū)別及其應(yīng)用場合,也會知曉 test/development/production 這幾種環(huán)境的區(qū)別;那也就避免了些不必要的麻煩。經(jīng)驗是慢慢積累的,過程可以幫我們汲取經(jīng)驗。等您自己應(yīng)用熟練了,我相信您能體會到測試帶來的好處。
### 下節(jié)預(yù)告 ###
??? 接下來我們依然是借助cucumber+rspec來驅(qū)動用戶登錄功能的開發(fā),看測試跟session和cookie打交道。如果有興趣,期待您能夠下次光臨!如果有好的建議和經(jīng)驗非常希望能夠與您交流,您可以在下面發(fā)表留言或者和我email聯(lián)系,我的郵箱是 xuliicom@gmail.com。
標簽:?Cucumber,?Rails,?Rspec,?TDD
Posted by 404轉(zhuǎn)載于:https://www.cnblogs.com/ToDoToTry/archive/2011/09/10/2173390.html
總結(jié)
以上是生活随笔為你收集整理的使用Cucumber+Rspec玩转BDD(2)——邮件激活的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 域名解析文件hosts文件是什么?如何修
- 下一篇: 大数据决策支持的优势