<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>mafai (MaFai)</title>
    <link>https://ruby-china.org/mafai</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>Coffee script &amp; Javascript 结构与组织</title>
      <description>&lt;p&gt;Title: Coffee script &amp;amp; Javascript organize
Tags: coffeescript,javascript
Slug: coffeescript
Excerpt: rails coffee script
Date: 2012-09-25&lt;/p&gt;
&lt;h2 id="CofeeScript &amp;amp; JavaScript Organize"&gt;CofeeScript &amp;amp; JavaScript Organize&lt;/h2&gt;
&lt;p&gt;我需要解决以下问题，相信其它朋友也会碰到，这是我翻查的结果，结论已经在我的项目用了，感觉不错，分享一下。原博文在 &lt;a href="http://scriptogr.am/mafai/post/coffeescript" rel="nofollow" target="_blank"&gt;http://scriptogr.am/mafai/post/coffeescript&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Target"&gt;Target&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;所有 js compile 在一个 js，不论是否需要&lt;/li&gt;
&lt;li&gt;不同页面需要运行的 js 才运行，不需要的不运行&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="Solution 1"&gt;Solution 1&lt;/h2&gt;&lt;h3 id="init不同namespace的js"&gt;init 不同 namespace 的 js&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;application.html.erb&lt;/code&gt; 这段不能用 coffee script，原因是动态 call init 的方法
可以再细分按 action name 或用正则表达式&lt;/p&gt;

&lt;p&gt;:javascript
      $(function() {
        #{"Window.#{params[:controller]}"}.init();
      });&lt;/p&gt;

&lt;p&gt;转换成的代码，当在访问 &lt;code&gt;/apples&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Window.apples.init()&lt;/p&gt;

&lt;p&gt;对应的 &lt;code&gt;apples.js.coffee&lt;/code&gt;，&lt;code&gt;like&lt;/code&gt;只是为了 demo，可以用 ujs 代替 &lt;/p&gt;

&lt;p&gt;Window.apples =
      like: (id) -&amp;gt;
            console.log("like apple:" + id)
            return
      init: () -&amp;gt; 
            console.log("init apple") 
            $(".apple").click () -&amp;gt;
              console.log("click me")
              console.log( $(this).attr("id") )
              Window.apples.like($(this).attr("id"))
              return
            return&lt;/p&gt;

&lt;p&gt;&lt;code&gt;oranges.js.coffee&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;oranges =
      init: () -&amp;gt; 
            console.log("init orange") 
            return
      eat: (id) -&amp;gt;
            console.log("eat orange:" + id)
            return&lt;/p&gt;

&lt;p&gt;当访问 &lt;code&gt;/apples&lt;/code&gt; 时，只会 init apples 的 js，如果是&lt;code&gt;/oranges&lt;/code&gt;，则只会 init orange js&lt;/p&gt;
&lt;h2 id="Solution 2"&gt;Solution 2&lt;/h2&gt;&lt;h3 id="方案1的变形，不过组织起来更舒服一占o，与方法1差不多"&gt;方案 1 的变形，不过组织起来更舒服一占 o，与方法 1 差不多&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;common.js.coffee&lt;/code&gt; 将 &lt;code&gt;apples, oranges&lt;/code&gt; 等作为 namespace&lt;/p&gt;

&lt;p&gt;Window.FruitsSite = 
      common: 
        init: () -&amp;gt; 
          console.log("init common")
      apples: 
        init: ()-&amp;gt; 
          console.log("init apples")
        like: (id) -&amp;gt;
          console.log("like apple id: " + id)
      oranges:
        init: () -&amp;gt; 
          console.log("init oranges")
        eat: (id) -&amp;gt;
          console.log("eat orange id: " + id)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;application.haml&lt;/code&gt; 动态按 controller 名去调用&lt;/p&gt;

&lt;p&gt;Window.FruitsSite["#{params[:controller]}"].init();&lt;/p&gt;
&lt;h2 id="Solution 3"&gt;Solution 3&lt;/h2&gt;&lt;h3 id="方案2的变形，将需要用到的 js 类放到 body中，同时细分到不同action页面级别"&gt;方案 2 的变形，将需要用到的 js 类放到 body 中，同时细分到不同 action 页面级别&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;body data-controller="apples" data-action="index""&amp;gt;&lt;/code&gt;
找出对应 apples 的 index 页面需要 init 的东西 init 一下&lt;/p&gt;

&lt;p&gt;或&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;body data-js="apples autoscroll"&amp;gt;&lt;/code&gt; Iint apples 和 autoscroll&lt;/p&gt;

&lt;p&gt;查找一下&lt;code&gt;data-js&lt;/code&gt;，然后找出对应的模块&lt;code&gt;init&lt;/code&gt;一下就好&lt;/p&gt;

&lt;p&gt;例子&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apples.js.coffee&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Window.FruitsSite = 
      apples: 
        index: 
          init: () -&amp;gt;
            console.log("init apples index page")
            return&lt;/p&gt;

&lt;p&gt;&lt;code&gt;applicatoin.haml&lt;/code&gt; 其实就是多一个层级而已，另外就是可以将这下面这段代码转成 coffee script，不用像上面两个例子写一些恶心 js 在页面上&lt;/p&gt;

&lt;p&gt;var $body = $("body");
     var controller = $body.attr("data-controller");
     var action = $body.attr("data-action");
     Window.FruitsSite[controller][action].init();&lt;/p&gt;
&lt;h2 id="Solution 4"&gt;Solution 4&lt;/h2&gt;&lt;h3 id="重写 jquery ready方法，利用body class 调用对应的ready"&gt;重写 jquery ready 方法，利用 body class 调用对应的 ready&lt;/h3&gt;
&lt;p&gt;重写 jquery ready 方法，看不明 proxy 的那个方法，不过没关系
这段代码来自于 &lt;a href="https://github.com/Verba/jquery-readyselector/blob/master/jquery.readyselector.js" rel="nofollow" target="_blank" title=""&gt;https://github.com/Verba/jquery-readyselector/blob/master/jquery.readyselector.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(function ($) {
       console.log("try to reset the ready function");
       var ready = $.fn.ready;
       $.fn.ready = function (fn) {
         console.log(this);
         console.log("Context:" + this.context);
         console.log(this.context);
         if (this.context === undefined) {
           console.log("no context defined");
           // The $().ready(fn) case.
           ready(fn);
         } else if (this.selector) {
           console.log("Selector:" + this.selector);
           ready($.proxy(function(){
             $(this.selector, this.context).each(fn);
           }, this));
         } else {
           console.log("Context have but no selector");
           ready($.proxy(function(){
             $(this).each(fn);
           }, this));
         }
       }
     })(jQuery);&lt;/p&gt;

&lt;p&gt;写不同的 ready 函数&lt;/p&gt;

&lt;p&gt;$('.apples.index').ready(function () {
       console.log("apple index page init");
     });
    $('.oranges.index').ready(function(){
       console.log("oranges index page init");
     });&lt;/p&gt;

&lt;p&gt;ready 这个方法就可有可无了&lt;/p&gt;

&lt;p&gt;$(function() {
    });&lt;/p&gt;

&lt;p&gt;body 中的 class 一定要以空间分隔&lt;/p&gt;

&lt;p&gt;%body{:class =&amp;gt; "#{params[:controller]} #{params[:action]}"}&lt;/p&gt;
&lt;h2 id="附"&gt;附&lt;/h2&gt;&lt;h3 id="其它的方法还有"&gt;其它的方法还有&lt;/h3&gt;
&lt;p&gt;用条件语句去控制，只是不那么高明&lt;/p&gt;

&lt;p&gt;$(body).hasClass("autoscroll")&lt;/p&gt;

&lt;p&gt;只当需要时才 new instance&lt;br&gt;
Window.FruitsSite.apples = new Apples;&lt;/p&gt;
&lt;h3 id="只能先实例化"&gt;只能先实例化&lt;/h3&gt;
&lt;p&gt;创建实例&lt;/p&gt;

&lt;p&gt;class Apples
      index:
        init: () -&amp;gt;
          console.log("init apples index page")
          return
    console.log(window.FruitsSite)
    window.FruitsSite.apples = new Apples&lt;/p&gt;

&lt;p&gt;简版&lt;/p&gt;

&lt;p&gt;window.FruitsSite.apples = 
      index:
        init: () -&amp;gt;
          console.log("init apples index page")
          return
    console.log(window.FruitsSite)&lt;/p&gt;
&lt;h2 id="CoffeeScript 整合 vim"&gt;CoffeeScript 整合 vim&lt;/h2&gt;
&lt;p&gt;到 &lt;a href="http://nodejs.org/" rel="nofollow" target="_blank" title=""&gt;node.js&lt;/a&gt; 下载一下 pkg(Mac)
装完后&lt;/p&gt;

&lt;p&gt;sudo npm install -g coffee-script&lt;/p&gt;

&lt;p&gt;mkdir -p ~/.vim/autoload ~/.vim/bundle 
    curl '&lt;a href="http://www.vim.org/scripts/download_script.php?src_id=16224" rel="nofollow" target="_blank" title=""&gt;www.vim.org/scripts/download_script.php?src_id=16224&lt;/a&gt;' 
      &amp;gt; ~/.vim/autoload/pathogen.vim&lt;/p&gt;

&lt;p&gt;修改一下 &lt;code&gt;~/.vimrc&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;filetype plugin indent on
    call pathogen#infect()&lt;/p&gt;

&lt;p&gt;安装 vim plugin&lt;/p&gt;

&lt;p&gt;cd ~/.vim/bundle
     git clone &lt;a href="https://github.com/kchmck/vim-coffee-script.git" rel="nofollow" target="_blank"&gt;https://github.com/kchmck/vim-coffee-script.git&lt;/a&gt;
     cd ~/.vim/bundle/vim-coffee-script
     git pull&lt;/p&gt;

&lt;p&gt;人肉安装（可选方法）&lt;/p&gt;

&lt;p&gt;unzip -od ~/.vim vim-coffee-script-HASH.zip&lt;/p&gt;

&lt;p&gt;用 vim 打开一个 coffee script 文件&lt;/p&gt;

&lt;p&gt;命令行，就可以将 coffee script 转成 js 分屏显示，爽啊&lt;/p&gt;

&lt;p&gt;:CoffeeCompile&lt;/p&gt;
&lt;h2 id="Reference"&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://topsecretproject.finitestatemachine.com/2010/04/how-to-organizing-javascript-in-ruby-on-rails/" rel="nofollow" target="_blank" title=""&gt;How-to: Organizing JavaScript in Ruby on Rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://stackoverflow.com/questions/1988986/what-is-the-best-way-to-organize-unobtrusive-javascript-across-multiple-pages" rel="nofollow" target="_blank" title=""&gt;What is the best way to organize unobtrusive JavaScript across multiple pages?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://georgebrock.github.com/talks/rubyconfua2010/" rel="nofollow" target="_blank" title=""&gt;Maintainable front-end code in Ruby on Rails applications&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.jerodsanto.net/2012/02/a-simple-pattern-to-namespace-and-selectively-execute-certain-bits-of-javascript-depending-on-which-rails-controller-and-action-are-active/" rel="nofollow" target="_blank" title=""&gt;A Simple Pattern to Namespace and Selectively Execute Certain Bits of JavaScript Depending on Which Rails Controller and Action are Active&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://viget.com/inspire/extending-paul-irishs-comprehensive-dom-ready-execution" rel="nofollow" target="_blank" title=""&gt;Extending Paul Irish’s comprehensive DOM-ready execution&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://vimeo.com/24391448" rel="nofollow" target="_blank" title=""&gt;Rails 3.1 javascript execution&lt;/a&gt;&lt;/p&gt;</description>
      <author>mafai</author>
      <pubDate>Wed, 28 Nov 2012 09:52:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/7145</link>
      <guid>https://ruby-china.org/topics/7145</guid>
    </item>
    <item>
      <title>Cucumber 经验总结</title>
      <description>&lt;p&gt;原文放在我的博客中 &lt;a href="http://scriptogr.am/mafai/post/cucumber-best-practices" rel="nofollow" target="_blank"&gt;http://scriptogr.am/mafai/post/cucumber-best-practices&lt;/a&gt;
Title: Cucumber 经验总结
Tag: cucumber,auto test,selenium
slug: cucumber-best-practices&lt;/p&gt;
&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;使用一段时间 cucumber 了，也看过一些书 The cucumber book, rspec book, 翻过一些 github 上开源项目的 cucumber，再结合自己项目和团队项目的经验做个总结&lt;/p&gt;
&lt;h2 id="Process 流程"&gt;Process 流程&lt;/h2&gt;&lt;h2 id="是否采用"&gt;是否采用&lt;/h2&gt;
&lt;p&gt;不是必须，看实际情况。我个人是非常推荐使用。总要考虑几个问题&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;投入成本，包括编写，和维护（保持更新），运行时间&lt;/li&gt;
&lt;li&gt;效果，是否需要持续集成&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;好处&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;当要 deploy 前，绿色一大片的感觉让你觉得更有安全感。&lt;/li&gt;
&lt;li&gt;自动化测试，减少大量的从复劳动&lt;/li&gt;
&lt;li&gt;代码变更时，自动化测试可追踪代码变更是否对现有功能有所影响&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="谁来编写"&gt;谁来编写&lt;/h2&gt;
&lt;p&gt;是否由测试人员撰写，我个人是推荐由开发人员编写，但每个 feature 的定义和 Scenario 由产品经理/测试人员是定义。&lt;/p&gt;
&lt;h3 id="Case 1测试人员编写"&gt;Case 1 测试人员编写&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;没再为功能性测试编写 Test case&lt;/li&gt;
&lt;li&gt;不能快速完成，因为 steps 需要了解背后实现（理论上不需要，但很多 exceptional case）&lt;/li&gt;
&lt;li&gt;需要开发人员支持，讲述给测试人员，一些 steps 的 trick way&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="Case 2开发人员编写"&gt;Case 2 开发人员编写&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;估算时带来好处，不需要为测试人员等待开发人员完成代码的时间作估算&lt;/li&gt;
&lt;li&gt;开发人员一般都不喜欢写测试用例，强制要求，也只能写出基本流程的测试（总比没有强）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="结论"&gt;结论&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;开发人员写 cucumber，代码重用性和质量会高很多，效率也高&lt;/li&gt;
&lt;li&gt;测试人员定制 scenario，和定义测试用例 test case 的道理一样，会全面很多&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="其它可能性"&gt;其它可能性&lt;/h3&gt;
&lt;p&gt;可以采取由开发人员轮流坐庄写 cucumber，不过这个我没机会实践，所以不知道效果会如何。&lt;/p&gt;
&lt;h2 id="采用BDD?"&gt;采用 BDD?&lt;/h2&gt;
&lt;p&gt;写 rspec 时，好流行安装 guard，然后代码改变时，同时运行相应的 rspec，理论上 cucumber 也可以这样做，我反对这样做。&lt;/p&gt;

&lt;p&gt;主要是 cucumber 运行的实在太慢，会严重打击你的积极性。另外，spork 和 guard spork 也是默认不对代码修改，运行 cucumber，这也是有道理的。&lt;/p&gt;
&lt;h2 id="代码"&gt;代码&lt;/h2&gt;&lt;h2 id="Naming convention 文件命名规范"&gt;Naming convention 文件命名规范&lt;/h2&gt;
&lt;p&gt;一般采用，对像加行为的描述，也用附加特定条件或范围的。也可以将行为放在前面。&lt;/p&gt;

&lt;p&gt;[module]&lt;u&gt;[action]&lt;/u&gt;[prep]_[condition].feature&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;user_signin_through_oauth_sina.feature&lt;/p&gt;

&lt;p&gt;post_add.feature&lt;/p&gt;

&lt;p&gt;post_delete_when_no_permission.feature&lt;/p&gt;
&lt;h2 id="Steps 不要overload web_steps"&gt;Steps 不要 overload web_steps&lt;/h2&gt;
&lt;p&gt;不要直接添加在 &lt;code&gt;web_steps.rb&lt;/code&gt; 和 &lt;code&gt;support/env.rb&lt;/code&gt;了，非常快就 overload 失控
实际上 web_steps 已经不再默认生成。&lt;/p&gt;

&lt;p&gt;可按功能拆分 steps:&lt;/p&gt;

&lt;p&gt;user_steps
post_steps
blog_steps&lt;/p&gt;
&lt;h2 id="是否使用Background"&gt;是否使用 Background&lt;/h2&gt;
&lt;p&gt;分两种观点，一种利用 background，可以抽取公用到 background
一种是不写 background，每个 scenario 独立&lt;/p&gt;

&lt;p&gt;这里没有定论，我编向是写 background，简单来讲，就是可重复使用前提条件，不好的地方就是降低可阅读性。但维护时会减轻工作量。如果不是为了生成的 cucumber 文档给产品经理看，我觉得还是使用 background 比较好。&lt;/p&gt;
&lt;h2 id="DRY 原则 (imperative or declarative)"&gt;DRY 原则 (imperative or declarative)&lt;/h2&gt;
&lt;p&gt;将相同步骤 cucumber 变成一个 step，即复用你的 cucumber steps
网上有大量的讨论，关于使用哪个好，我个人偏好是 imperative，原因是，大量的 declarative steps 会造成维护困难，改起来非常累&lt;/p&gt;

&lt;p&gt;可以看看这两篇&lt;/p&gt;

&lt;p&gt;&lt;a href="http://watirmelon.com/2010/12/10/cucumber-imperative-or-declarative-that-is-the-question/" rel="nofollow" target="_blank" title=""&gt;cucumber-imperative-or-declarative-that-is-the-question/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html" rel="nofollow" target="_blank" title=""&gt;Imperative vs declarative scenarios in user story
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="Cucumber steps基本规范"&gt;Cucumber steps 基本规范&lt;/h2&gt;
&lt;p&gt;Cucumber 的 steps 尽量用人类看得懂的语言，所有 正则表达式，css, xpath，等各种代码不应该在 cucumber steps 中出现。需要时，就另外写一个 steps 封装一下就好。 &lt;/p&gt;
&lt;h2 id="文件目录结构，附开源项目的做法"&gt;文件目录结构，附开源项目的做法&lt;/h2&gt;
&lt;p&gt;可以将按模块划分，将相应的 features 放入子目录，如果 features 少，也可以省略。但不要用 sprint11, 或 milestone11 这种目录，一定要的话，可以写在 tag 上，如 s@sprint11，否则维护起来就杯具了。&lt;/p&gt;
&lt;h2 id="是否按标准的User story写背景和description"&gt;是否按标准的 User story 写背景和 description&lt;/h2&gt;
&lt;p&gt;一种写法是 As xxx Role, I would do yyy so that zzz
这种写法会对项目新成员会有一定的帮助，不好的地方就是维护成本了，所以那几个开源项目都没有严格按照 User story 的写法，只是简单完成即可。我偏向是简单写，直观明了。&lt;/p&gt;
&lt;h3 id="diaspora"&gt;diaspora&lt;/h3&gt;
&lt;p&gt;命名规范 &lt;code&gt;[action]_[item].feature&lt;/code&gt;
没有子目录&lt;/p&gt;

&lt;p&gt;accepts_invitation.feature         edits_profile.feature              oembed.feature
        activity_stream.feature            follows_tags.feature               photo_lightbox.feature
        aspect_navigation.feature          invitations.feature                posts_from_main_page.feature
        blocks_user.feature                logged_out_browsing.feature        reshare.feature
        change_email.feature               logs_in_and_out.feature            show_more.feature
        change_password.feature            manages_aspects.feature            signs_up.feature
        closes_account.feature             mentions.feature                   step_definitions
        comments.feature                   mentions_from_profile_page.feature stops_following_users.feature
        connects_users.feature             mobile.feature                     support
        conversations.feature              not_safe_for_work.feature          tags.feature
        download_photos.feature            notifications.feature              tags_and_comments.feature&lt;/p&gt;
&lt;h5 id="Step"&gt;Step&lt;/h5&gt;
&lt;p&gt;大体都差不多和 teambox 一样， [module]_sptes.rb&lt;/p&gt;

&lt;p&gt;aspects_steps.rb       factory_steps.rb       oembed.rb              stream_steps.rb
    comment_steps.rb       lightbox_steps.rb      posts_steps.rb         template_steps.rb
    conversations_steps.rb mention_steps.rb       profile_steps.rb       uri-step.rb
    custom_web_steps.rb    message_steps.rb       scope_steps.rb         user_steps.rb
    debug_steps.rb         mobile_steps.rb        session_steps.rb       web_steps.rb&lt;/p&gt;
&lt;h4 id="teambox"&gt;teambox&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;[item]_[action].feature&lt;/code&gt; 我偏向这种，因为分类清楚点&lt;/p&gt;

&lt;p&gt;project_archive.feature              upload_rename.feature
        project_create.feature               user_change_password.feature
        project_delete.feature               user_edit_profile.feature
        project_invitations.feature          user_edit_settings.feature
        project_join.feature                 user_first_steps.feature
        project_leave.feature                user_login.feature
        project_public.feature               user_logout.feature
        public_downloads.feature             user_profile.feature
        search_projects.feature              user_reset_password.feature
        sidebar.feature                      user_signup.feature&lt;/p&gt;
&lt;h5 id="Step"&gt;Step&lt;/h5&gt;
&lt;p&gt;action_steps.rb             organization_steps.rb       task_list_steps.rb
        activity_steps.rb           page_steps.rb               task_list_template_steps.rb
        authentication_steps.rb     pickle_steps.rb             task_reminders_steps.rb
        comment_steps.rb            project_steps.rb            task_steps.rb
        conversation_steps.rb       public_downloads_steps.rb   teamboxdata_steps.rb
        db_steps.rb                 relative_time_steps.rb      time_steps.rb
        email_steps.rb              reset_password_steps.rb     upload_steps.rb
        folder_steps.rb             search_steps.rb             user_steps.rb
        invitation_steps.rb         see_within_steps.rb         watchers_steps.rb
        oauth_steps.rb              sidebar_steps.rb            web_steps.rb&lt;/p&gt;
&lt;h3 id="Cucumber样例"&gt;Cucumber 样例&lt;/h3&gt;&lt;h4 id="diaspora"&gt;diaspora&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;features/activity_stream.feature&lt;/p&gt;

&lt;p&gt;Feature: The activity stream
      Scenario: Sorting
        Given a user with username "bob" #没有使用 pickle
        When I try to sign in manually" #一起来看看 sign in 吧&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;features/step_definitions/session_steps.rb&lt;/p&gt;

&lt;p&gt;When /^I try to sign in manually$/ do
      manual_login
    end&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;features/support/user_cuke_helpers.rb&lt;/p&gt;

&lt;p&gt;# use the &lt;a href="/me" class="user-mention" title="@me"&gt;&lt;i&gt;@&lt;/i&gt;me&lt;/a&gt; user to perform a manual login via the sign_in page
    def manual_login
        visit login_page 
        login_as &lt;a href="/me.username" class="user-mention" title="@me.username"&gt;&lt;i&gt;@&lt;/i&gt;me.username&lt;/a&gt;, &lt;a href="/me.password" class="user-mention" title="@me.password"&gt;&lt;i&gt;@&lt;/i&gt;me.password&lt;/a&gt; #login_as 其实就是 wrap 了 fill_in 方法
    end&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;features/support/paths.rb&lt;/p&gt;

&lt;p&gt;def login_page
        path_to "the new user session page"
      end&lt;/p&gt;

&lt;p&gt;def path_to(page_name)
        when /^the ([\w ]+) page$/
        send("#{$1.gsub(/\W+/, '_')}_path")&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="Cucumber 的测试附件存放位置"&gt;Cucumber 的测试附件存放位置&lt;/h3&gt;
&lt;p&gt;不要再使用 app/assets 下的东西了。&lt;/p&gt;

&lt;p&gt;➜  sample_files git:(dev) 
    ls /Users/mafai/Projects/teambox/features/support/sample_files
    dragon.jpg tiger.jpg&lt;/p&gt;
&lt;h2 id="其它工具，cucumber好帮手"&gt;其它工具，cucumber 好帮手&lt;/h2&gt;&lt;h2 id="Capybara"&gt;Capybara&lt;/h2&gt;
&lt;p&gt;这个应该是默认的了，The cucumber book 中，提及到为什么不用 webrat 和 selenium，在这里就不废话了。&lt;/p&gt;
&lt;h2 id="Pickle"&gt;Pickle&lt;/h2&gt;
&lt;p&gt;这个 gem 帮你省确了大量的 module 创建的 steps，用上好，你的 steps 会减少好多，但同时，你的 cucumber 描述就变得不是那么容易阅读。
！注意，开源项目引入了这个 gem，但并不使用它，如果你想你的 cucumber steps 变得更简洁和易于阅读，pickle 不是你工具。&lt;/p&gt;
&lt;h2 id="Factory girl"&gt;Factory girl&lt;/h2&gt;
&lt;p&gt;用上 factory girl 建立对像时，可以设定默认值或指定规则的 value，也可以省下不少代码&lt;/p&gt;

&lt;p&gt;Pickle.configure do |config|
      #config.adapters = [:machinist]
      config.adapters = [:factory_girl] #以后通过 factory girl 来创建对像
      #config.map 'I', 'myself', 'me', 'my', :to =&amp;gt; 'user: "me"'
    end&lt;/p&gt;

&lt;p&gt;#所有的 module 都须要在 FactoryGirl 登记一下，否则在运行时，pickle 会有问题，undefined steps
    FactoryGirl.define do
          factory :post do
            title { "Dummy title" }
            body  { "#{title}!" }
          end
        end&lt;/p&gt;
&lt;h2 id="Spork"&gt;Spork&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;spork cucumber&lt;/code&gt; Spork 可以先 preload 整个测试环境&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cucumber -r features features/posts.feature --drb&lt;/code&gt; 然后就享受一下速度吧，但注意这样做好，如果你将代码修改后，其实 spork 不会懂得 reload 的。&lt;/p&gt;
&lt;h2 id="Guard spork"&gt;Guard spork&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;guard init spork&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;生成&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/Users/mafai/Projects/compass/Guardfile&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;你会发觉，即使代码出错，也不会影响 cucumber 的测试，主要是默认缓存了 class 的原因&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config.cache_classes = false&lt;/code&gt; #改成 false 就好了&lt;/p&gt;

&lt;p&gt;你也可以选择 watch 所有的*.rb 文件，restart spork server，不过这实践不是这些工具所提倡的。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://stackoverflow.com/questions/5855587/spork-is-really-great-but-how-can-i-get-it-to-refresh-validations-and-other-cod" rel="nofollow" target="_blank" title=""&gt;http://stackoverflow.com/questions/5855587/spork-is-really-great-but-how-can-i-get-it-to-refresh-validations-and-other-cod&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="使用webkit代替selenium"&gt;使用 webkit 代替 selenium&lt;/h2&gt;
&lt;p&gt;使用 webkit 做前端测试，速度会提升，因为不需要打开 browser，只要在&lt;code&gt;env.rb&lt;/code&gt;用上&lt;code&gt;Capybara.javascript_driver = :webkit&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="Trick"&gt;Trick&lt;/h2&gt;&lt;h2 id="默认路径 path"&gt;默认路径 path&lt;/h2&gt;
&lt;p&gt;如果是一些标准命名的 page，再不须添加 path 了，否则真的很烦&lt;/p&gt;

&lt;p&gt;else
         begin
           page_name =~ /the (.*) page/
           path_components = $1.split(/\s+/) 
           self.send(path_components.push('path').join('&lt;u&gt;').to_sym) #这个 send 方法我不清楚是调用哪一个，其实就是将 user profile page，转换成 user_profile_path，这将其转成 symbol 字符串 
         rescue Object =&amp;gt; e
           raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
             "Now, go and add a mapping in #{&lt;/u&gt;&lt;u&gt;FILE&lt;/u&gt;_}"
         end&lt;/p&gt;
&lt;h2 id="运行单个cucumber"&gt;运行单个 cucumber&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;cucumber features/billing/credit_card.feature:104 -f progress -r features&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="Cucumber rerun 重复运行出错的scenario"&gt;Cucumber rerun 重复运行出错的 scenario&lt;/h2&gt;
&lt;p&gt;$ cucumber -f rerun --out rerun.txt&lt;/p&gt;

&lt;p&gt;#结果
    features/one.feature:367 features/another.feature:91:117&lt;/p&gt;
&lt;h2 id="启动时清数据"&gt;启动时清数据&lt;/h2&gt;
&lt;p&gt;改一下 &lt;code&gt;hook.rb&lt;/code&gt;就好&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before do
  $redis.flushall
end

After do
  $redis.flushall
  # clean out the Solr index after each scenario
  Sunspot.remove_all!
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Devise登录"&gt;Devise 登录&lt;/h2&gt;
&lt;p&gt;见到一种较特别的写法，就是在测试环境时，动态添加一个 route 和 action，具体看 diaspora 项目的代码。其它都是模似用户的 steps 做的。&lt;/p&gt;
&lt;h2 id="其它Topic，但不在此讨论"&gt;其它 Topic，但不在此讨论&lt;/h2&gt;&lt;h2 id="多台机器分布式运行cucumber，提速"&gt;多台机器分布式运行 cucumber，提速&lt;/h2&gt;&lt;h2 id="分别打开IE, FireFox, Opera, Chrome等浏览器跑界面，进行对layout的快速review"&gt;分别打开 IE, FireFox, Opera, Chrome 等浏览器跑界面，进行对 layout 的快速 review&lt;/h2&gt;&lt;h2 id="结合bamboo server做持续集成测试"&gt;结合 bamboo server 做持续集成测试&lt;/h2&gt;&lt;h2 id="Reference"&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.elabs.se/blog/15-you-re-cuking-it-wrong" rel="nofollow" target="_blank" title=""&gt;http://www.elabs.se/blog/15-you-re-cuking-it-wrong&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://eggsonbread.com/2010/09/06/my-cucumber-best-practices-and-tips/" rel="nofollow" target="_blank" title=""&gt;http://eggsonbread.com/2010/09/06/my-cucumber-best-practices-and-tips/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html" rel="nofollow" target="_blank" title=""&gt;http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.relishapp.com/rspec/" rel="nofollow" target="_blank" title=""&gt;Rspec specification&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://spin.atomicobject.com/2011/06/02/never-say-click-good-cucumber-system-testing-practices/" rel="nofollow" target="_blank" title=""&gt;http://spin.atomicobject.com/2011/06/02/never-say-click-good-cucumber-system-testing-practices/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://www.cowboycoded.com/2011/01/05/better-web-steps-for-cucumber-with-capybara/" rel="nofollow" target="_blank" title=""&gt;http://www.cowboycoded.com/2011/01/05/better-web-steps-for-cucumber-with-capybara/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit" rel="nofollow" target="_blank" title=""&gt;https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://robots.thoughtbot.com/post/189412598/five-ridiculously-awesome-cucumber-and-webrat" rel="nofollow" target="_blank" title=""&gt;http://robots.thoughtbot.com/post/189412598/five-ridiculously-awesome-cucumber-and-webrat&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://collectiveidea.com/blog/archives/2011/05/25/testing-with-sunspot-and-cucumber/" rel="nofollow" target="_blank" title=""&gt;http://collectiveidea.com/blog/archives/2011/05/25/testing-with-sunspot-and-cucumber/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://www.cowboycoded.com/2011/01/05/better-web-steps-for-cucumber-with-capybara/" rel="nofollow" target="_blank" title=""&gt;http://www.cowboycoded.com/2011/01/05/better-web-steps-for-cucumber-with-capybara/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://blog.trydionel.com/2010/02/06/testing-sunspot-with-cucumber/" rel="nofollow" target="_blank" title=""&gt;http://blog.trydionel.com/2010/02/06/testing-sunspot-with-cucumber/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>mafai</author>
      <pubDate>Tue, 27 Nov 2012 13:49:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/7119</link>
      <guid>https://ruby-china.org/topics/7119</guid>
    </item>
    <item>
      <title>OAuth 有不？</title>
      <description>&lt;p&gt;正在弄 Ruby China Android Client
啥时加上 OAuth?&lt;/p&gt;</description>
      <author>mafai</author>
      <pubDate>Thu, 15 Nov 2012 23:01:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/6802</link>
      <guid>https://ruby-china.org/topics/6802</guid>
    </item>
  </channel>
</rss>
