访问次数
我的另一个总结性博客: todototry 大米粒

使用Cucumber+Rspec玩转BDD(5)——安全退出


### 温故知新 ###


为了保护用户的隐私,限制特定资料的访问,前面我们给系统增加了登录功能;紧接着,又为了方便用户在一段时间之内不必重复登录操作,我们实现了用户的持久登录状态,即“记住我”功能。如果浏览器未关闭,或者用户一直处于在线状态,而用户自己并没有使用这台设备,很显然,这对用户的帐号是非常危险的;基于此,系统应该提供一个给用户手工注销在线状态退出站点的功能。

为了获得更好的阅读体验,读者朋友们可以在这里下载源码:http://github.com/404/bdd_user_demo/tree/master


### 新建工作分支 ###

$ git checkout -b user_logout


### 用户注销退出功能 ###

  1.提供一个“退出”链接,用户登录后点击该链接可以注销在线状态;
  2.用户登录并勾选记住我后,点击“退出”链接可以注销在线状态,下次访问的时候将不再自动登录。

下面来编写对应于以上功能的故事场景。


### 故事用例之用户注销退出 ###

$ gedit features/user_logout.feature

功能: 用户安全退出
  为了保护我的帐号不被他人非法使用
  作为一名已经登录的在线用户
  我希望能够安全退出
  
  场景: 用户注销在线状态
    假如 我已经使用<404/xuliicom@gmail.com/password>注册过且已经激活了帐号
    当 我以<xuliicom@gmail.com/password>这个身份登录
    那么 我应该成功登录网站
    当 我退出网站
    那么 我应该看到<您已经安全退出>的提示信息
    而且 我应该尚未登录

  场景: 用户在持久在线状态下退出
    假如 我已经使用<404/xuliicom@gmail.com/password>注册过且已经激活了帐号
    当 我以<xuliicom@gmail.com/password>这个身份登录并勾选<记住我>
    那么 我应该成功登录网站
    当 我退出网站
    那么 我应该看到<您已经安全退出>的提示信息
    而且 我应该尚未登录
    当 我关闭网页下次再来访问的时候
    那么 我应该尚未登录


可以把一个feature文件当作一份书面需求的电子版,如果你觉得文件开头几句没什么用(为了...作为...我希望...),那直接在那里罗列出功能简要咯,故事场景就当作详尽的需求来写。例如:

功能: 用户安全退出
  1.提供一个“退出”链接,用户登录后点击该链接可以注销在线状态;
  2.用户登录并勾选记住我后,点击“退出”链接可以注销在线状态,下次访问的时候将不再自动登录。

  
  场景1
    ...

  场景2
    ...

若真那样,说不定可以节约不少会议时间,因为用于测试的故事文本(feature文件)完全可以替代现实中的功能需求书,而且更加灵活。再向前一步,就可以直接把客户说的需求整理到测试中去,完了发动测试引擎一直在那转动着,开发人员和测试引擎凑到一块儿玩结对编程……敏捷开发,我们还需要文档么?

回头干正事儿,保存 user_logout.feature。运行测试看看它能告诉我们应该做些什么,

$ ruby script/cucumber -l zh-CN features/user_logout.feature




### 编写测试脚本 ###

如上图所示,我们需要为“当 我退出网站”定义所需的运行脚本。

$ gedit features/step_definitions/user_steps.rb

# user logout

When /^我退出网站$/ do
  visit '/logout', :delete
end


保存user_steps.rb。运行测试,

$ ruby script/cucumber -l zh-CN features/user_logout.feature




### 配置登出路由(logout_path) ###

没有找到"/logout"这一访问路径,因为我们还没有在 routes.rb 配置文件中定义"/logout"。修改 routes.rb 文件,添加这条路由信息,

$ gedit config/routes.rb

为了sessions资源看起来有紧凑的结构,我们稍微变换了login_path的定义,与logout_path整合到了一起。

  map.with_options :controller => 'sessions' do |page|
    page.login '/login', :action => 'new'
    page.logout '/logout', :action => 'destroy'
  end



### 实现用户注销退出 ###

既然 "/logout" 路由指向了 SessionsController 类的 destroy 方法,那么我们还需要编写 destroy 方法的具体实现。

$ gedit app/controllers/sessions_controller.rb

添加 destroy 方法,

  def destroy
    forget(current_user)
    reset_session
    flash[:notice] = "您已经安全退出!"
    redirect_to login_path
  end


在 private 之后添加 forget 方法,

  def forget(user)
    user.forget_me! if user
    cookies.delete :remember_token
  end



### 删除服务端的remember_token ###

在forget方法中,程序调用了User实例对象的forget_me!方法;然后清除了当前客户端与服务端会话的cookies;仅仅清除客户端的cookies还不够,服务器上的也应该一并删除。

$ gedit app/models/user.rb

添加forget_me!方法,

  # 删除数据库里边的remember_token
  def forget_me!
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end


保存 user.rb。运行测试,

$ ruby script/cucumber -l zh-CN features/user_logout.feature



测试通过!


### 亲临现场 ###

为了方便登录用户能够以鼠标点击的方式退出站点,也为了方便开发人员自己手工测试,此时还需要给登录用户提供一个用于注销退出的链接。在之前设置的访问控制中,用户资料显示页面只能在用户登录以后才可见,我们可以将此退出链接加到这个页面中。

$ gedit app/views/users/show.html.erb

在该模板文件末尾加上如下一段代码,

<p>
  <%= link_to "安全退出", logout_path %>
</p>


保存 show.html.erb。打开 Web Server 手工测试看看,

$ ruby script/server

打开浏览器,登录到用户资料查看页面;



点击“安全退出”链接,我们看到系统将我们带到了用户登录页面;



再次刷新刚才那个用户资料显示页面,系统给我们呈现的是一张登录表单;事实说明我们已经成功登出了。




### 下节预告 ###

接下来的一章里,我们将会回到发送邮件的操作上,与之前发送激活邮件不同的是,下一次将会给忘记密码的用户发送一封用于找回密码的邮件。所以,我们下一章的主题就是找回密码。


### 提交工作成果到GIT仓库 ###

$ git status
$ git add .
$ git commit -m "A user can be logout."
$ git checkout master
$ git merge user_logout
$ git branch -d user_logout
$ git tag v5


(注意,真正的开发中可不是到功能开发完毕了才commit,而是边开发边add和commit。为了方便演示编码过程,文章中没有一一列举。)

标签: CucumberRailsRspecTDD

posted @ 2011-09-10 22:09  fandyst  阅读(401)  评论(0编辑  收藏  举报