Ruby cucumber basic

出自Eddie's Courses

跳轉到: 導航, 搜尋

Ryan Bates在Railscast上有很精彩完整的影片展示,本文內容大多是依照它影片介紹再加一些自己的修改,更精彩的內容可前往Railscast

目錄

簡介

安裝

使用cucumber來做測試會需要以下module,不過有rubygem的話安裝是相當容易的。

  • rspec 1.3.0
  • rspec-rails 1.3.2
  • webrat 0.7.0
  • cucumber 0.6.4
  • cucumber-rails 0.3.0

附註:以上版本可能會隨著時間而有不同的語法更動,細節請見各個gem的網站說明

第一步

透過script/generate指令可以產生一些cucumber所需要的目錄及檔案

 % script/generate cucumber
     create  config/cucumber.yml
     create  config/environments/cucumber.rb
     create  script/cucumber
     create  features/step_definitions
     create  features/step_definitions/web_steps.rb
     create  features/support
     create  features/support/paths.rb
     create  features/support/env.rb
     create  lib/tasks
     create  lib/tasks/cucumber.rake

開始進行

寫腳本

我們要來開始寫第一個故事腳本了,檔名不限定要怎麼取,附檔名是.feature。例如我們想要寫一個腳本來測試看看文章管理的功能,所以檔名就取做articles_manage.feature

檔案:features/articles_manage.feature

 Feature: Manage Articles
   In order to make a blog
   As an author
   I want to create and manage articles
 
   Scenario: Articles List
     Given I have articles titled Pizza, Breadsticks
     When I go to the list of articles
     Then I should see "Pizza"
     And I should see "Breadsticks"

如何? 寫起來像一般的英文句子吧! 我想這時候你應該會懷疑這真的假的,我們寫這樣電腦就看得懂? 當然目前還是看不懂的,有些地方還是需要我們幫忙定義才行。

進行測試

% cucumber features

然後會出現以下訊息

 Feature: Manage Articles
   In order to make a blog
   As an author
 
   Scenario: Articles List                           # features/manage_article.feature:6
     Given I have articles titled Pizza, Breadsticks # features/manage_article.feature:7
       Undefined step: "I have articles titled Pizza, Breadsticks" (Cucumber::Undefined)
       features/manage_article.feature:7:in `Given I have articles titled Pizza, Breadsticks'
     When I go to the list of articles               # features/step_definitions/web_steps.rb:18
     Then I should see "Pizza"                       # features/step_definitions/web_steps.rb:142
     And I should see "Breadsticks"                  # features/step_definitions/web_steps.rb:142
 
 1 scenario (1 undefined)
 4 steps (3 skipped, 1 undefined)
 0m0.114s
 
 You can implement step definitions for undefined steps with these snippets:
 
 Given /^I have articles titled Pizza, Breadsticks$/ do
   pending # express the regexp above with the code you wish you had
 end

我們在Scenario裡有4個steps,其中3個被skip掉了(因為前面有錯誤,所以暫不進行測試),而錯誤的那個原因是"undefined",因為我們還沒有定義什麼叫做"I have articles titled Pizza, Breadsticks",所以接下來要來定義它。

定義步驟

在features/step_definitions這個資料夾裡新增一個article_steps.rb(檔名沒限定,到時候系統會自己掃一次這裡的*.rb檔),基本上它是.rb檔,所以可以在裡面寫一些ruby code。如果有注意到在step_definitions目錄裡其實還有個web_steps.rb,這個是事先幫我們定義好的一些步驟。 檔名:features/step_definitions/article_steps.rb

  Given /^I have articles titled Pizza, Breadsticks$/ do
    pending # express the regexp above with the code you wish you had
  end

但為了讓這個定義能不只只收"Pizza"或是"Breadsticks"這兩種title,這裡可以用Regular Expression改寫一下

  Given /^I have articles titled (.+)$/ do |titles|
    titles.split(", ").each do |title|
      Article.create!(:title => title)
    end
  end

繼續進行測試

我們修正了之前未定義的錯誤訊息,接著再繼續重複測試的步驟:

 Syphilis% cucumber features
 Using the default profile...
 Feature: Manage Articles
   In order to make a blog
   As an author
   I want to create and manage articles
 
   Scenario: Articles List                           # features/manage_article.feature:6
     Given I have articles titled Pizza, Breadsticks # features/step_definitions/article_steps.rb:1
       uninitialized constant Article (NameError)
       ./features/step_definitions/article_steps.rb:3
       ./features/step_definitions/article_steps.rb:2:in `each'
       ./features/step_definitions/article_steps.rb:2:in `/^I have articles titled (.+)$/'
       features/manage_article.feature:7:in `Given I have articles titled Pizza, Breadsticks'
     When I go to the list of articles               # features/step_definitions/web_steps.rb:18
     Then I should see "Pizza"                       # features/step_definitions/web_steps.rb:142
     And I should see "Breadsticks"                  # features/step_definitions/web_steps.rb:142
 
 Failing Scenarios:
 cucumber features/manage_article.feature:6 # Scenario: Articles List
 
 1 scenario (1 failed)
 4 steps (1 failed, 3 skipped)
 0m0.115s

還是跳了一堆錯誤訊息! 別擔心,仔細看的話就會發現其實錯誤訊息跟剛剛不一樣了,現在是Article這個model找不到(因為我們也還沒建)。 基本上這種測試先行的開發方式,不管是一般的TDD或是這裡的BDD,一開始都是先寫好我們期望看到的結果,然後不斷的測試、不斷的修改,再繼續的測試,再繼續的修改,直到程式碼可以通過我們列出來的測試條件為止。(其實我個人覺得測試先行開發的另一個收獲就是會比較不怕看到一堆錯誤訊息噴出來)。

繼續修改

上一個步驟寫到我們還沒有建立Article這個model,一般我們只要一個指令就能完成

 % script/generate model article title:string content:text
   exists  app/models/
   exists  test/unit/
   exists  test/fixtures/
   create  app/models/article.rb
   create  test/unit/article_test.rb
   create  test/fixtures/articles.yml
   create  db/migrate
   create  db/migrate/20100503084033_create_articles.rb

但是這裡我們不這樣做,我們改給不同的參數來產生model

 % script/generate rspec_model article title:string content:text
   create  app/models/
   create  spec/models/
   create  spec/fixtures/
   create  app/models/article.rb
   create  spec/models/article_spec.rb
   create  spec/fixtures/articles.yml
   exists  db/migrate
   create  db/migrate/20100503084107_create_articles.rb

注意到這兩個參數的差別嗎? 基本上該產生的model跟migration都一樣,差別在於第一種方法會以預設的測試框架來產生相關檔案架構(例如test/unit),而使用rspec_model的話則是產生一些以rspec為測試框架的檔案架構。這個rspec_開頭的參數後面還會常用到,主要就是用來產生rspec專用的檔案。

做一下db:migrate

 % rake db:migrate

接下來也把schema複製一份到測試環境的DB

 % rake db:test:clone

然後可以看一下資料庫,看看是不是真的有把table建起來了

繼續測試

 % cucumber features                                      
 Feature: Manage Articles
   In order to make a blog
   As an author
   I want to create and manage articles
 
   Scenario: Articles List                           # features/manage_article.feature:6
     Given I have articles titled Pizza, Breadsticks # features/step_definitions/article_steps.rb:1
     When I go to the list of articles               # features/step_definitions/web_steps.rb:18
       Can't find mapping from "the list of articles" to a path.
       Now, go and add a mapping in /private/tmp/blog/features/support/paths.rb (RuntimeError)
       ./features/support/paths.rb:22:in `path_to'
       ./features/step_definitions/web_steps.rb:19:in `/^(?:|I )go to (.+)$/'
       features/manage_article.feature:8:in `When I go to the list of articles'
     Then I should see "Pizza"                       # features/step_definitions/web_steps.rb:142
     And I should see "Breadsticks"                  # features/step_definitions/web_steps.rb:142
 
 Failing Scenarios:
 cucumber features/manage_article.feature:6 # Scenario: Articles List
 
 1 scenario (1 failed)
 4 steps (1 failed, 2 skipped, 1 passed)
 0m0.132s

測試結果又不同了,這次有1個pass,然後有1個錯誤,2個被skip。這次的錯誤訊息是「Can't find mapping from "the list of articles" to a path.」,所以又要來定義一下這個path了。跟前面的step_definitions同一層級有個support目錄,裡面有一個paths.rb,打開它準備來新增一些path的定義。

  when /the list of articles/
    articles_path

這意思是說當遇到"the list of articles"的時候,就是會去articles_path。那什麼是"articles_path"? 這個在rails裡的config/routes.rb可以加一行:

  map.resource :articles

這裡也可以直接用一般的路徑表示法,不一定要用restful的方式表示,不過用restful是個不錯的方式(只是後面可能需要照restful的方式實作一些action),看個人習慣。

修改好了,繼續測試:

 % cucumber features
   Feature: Manage Articles
     In order to make a blog
     As an author
     I want to create and manage articles
 
     Scenario: Articles List                           # features/manage_article.feature:6
       Given I have articles titled Pizza, Breadsticks # features/step_definitions/article_steps.rb:1
       When I go to the list of articles               # features/step_definitions/web_steps.rb:18
         uninitialized constant ArticlesController (NameError)
         (eval):2:in `visit'
         ./features/step_definitions/web_steps.rb:19:in `/^(?:|I )go to (.+)$/'
         features/manage_article.feature:8:in `When I go to the list of articles'
       Then I should see "Pizza"                       # features/step_definitions/web_steps.rb:142
       And I should see "Breadsticks"                  # features/step_definitions/web_steps.rb:142
 
   Failing Scenarios:
   cucumber features/manage_article.feature:6 # Scenario: Articles List
 
   1 scenario (1 failed)
   4 steps (1 failed, 2 skipped, 1 passed)
   0m0.086s

這次是錯在沒有Controller(因為也還沒建立),那就繼續建立,一步一步的修改錯誤訊息。如同model的建立一樣,這裡我們也用rspec_controller來新增一個controller。

 % script/generate rspec_controller articles index
     exists  app/controllers/
     exists  app/helpers/
     create  app/views/articles
     create  spec/controllers/
     create  spec/helpers/
     create  spec/views/articles
     create  spec/controllers/articles_controller_spec.rb
     create  spec/helpers/articles_helper_spec.rb
     create  app/controllers/articles_controller.rb
     create  app/helpers/articles_helper.rb
     create  spec/views/articles/index.html.erb_spec.rb
     create  app/views/articles/index.html.erb

接下來可以繼續執行測試看看,如果你上面的path用的是restful的方式的話,這裡面可能會要求你實作show action跟show template。

 % cucumber features
   Feature: Manage Articles
     In order to make a blog
     As an author
     I want to create and manage articles
 
 Scenario: Articles List                           # features/manage_article.feature:6
   Given I have articles titled Pizza, Breadsticks # features/step_definitions/article_steps.rb:1
   When I go to the list of articles               # features/step_definitions/web_steps.rb:18
   Then I should see "Pizza"                       # features/step_definitions/web_steps.rb:142
     expected the following element's content to include "Pizza":
     .
     <false> is not true. (Test::Unit::AssertionFailedError)
     features/manage_article.feature:9:in `Then I should see "Pizza"'
   And I should see "Breadsticks"                  # features/step_definitions/web_steps.rb:142
 
 Failing Scenarios:
 cucumber features/manage_article.feature:6 # Scenario: Articles List
 
 1 scenario (1 failed)
 4 steps (1 failed, 1 skipped, 2 passed)
 0m0.083s

我們又往通過測試的目標前進一步了,接下來是應該要看到"Pizza"字樣,但沒看到....因為我們還沒有在view的地方把內容秀出來,所以我們來改一下controller跟view的部份。

檔案:app/controllers/articles_controller.rb

  class ArticlesController < ApplicationController
    def index
    end
 
    def show
      @articles = Article.all
    end
  end

檔案:app/views/articles/show.html.erb

  <% for article in @articles %>
    <%=h article.title %>
  <% end %>

接下來再繼續測試

 % cucumber features
 Feature: Manage Articles
   In order to make a blog
   As an author
   I want to create and manage articles
 
   Scenario: Articles List                           # features/manage_article.feature:6
     Given I have articles titled Pizza, Breadsticks # features/step_definitions/article_steps.rb:1
     When I go to the list of articles               # features/step_definitions/web_steps.rb:18
     Then I should see "Pizza"                       # features/step_definitions/web_steps.rb:142
     And I should see "Breadsticks"                  # features/step_definitions/web_steps.rb:142
 
 1 scenario (1 passed)
 4 steps (4 passed)
 0m0.081s

四個步驟全過了!! 恭喜你,基本上這樣就完成了。

但這樣有點簡單,所以接下來我們再看一下進階篇,看看怎麼樣做複雜一點的,例如自動填表單的測試。

其它

個人工具