Ruby cucumber basic
出自Eddie's Courses
Ryan Bates在Railscast上有很精彩完整的影片展示,本文內容大多是依照它影片介紹再加一些自己的修改,更精彩的內容可前往Railscast
目錄 |
簡介
- 相關網站
- BDD(Behavior Driven Development) http://en.wikipedia.org/wiki/Behavior_Driven_Development
- Rspec官網 http://rspec.info/
- Cucumber官網 http://cukes.info/
- 名詞介紹
- RSpec:A BDD(Behaviour Driven Development) framework for Ruby.
- Cucumber:is RSpec's "Story Runner"
安裝
使用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
四個步驟全過了!! 恭喜你,基本上這樣就完成了。
但這樣有點簡單,所以接下來我們再看一下進階篇,看看怎麼樣做複雜一點的,例如自動填表單的測試。
其它
- 若有使用TextMate可安裝cucumber boundles
