Showing posts with label BDD. Show all posts
Showing posts with label BDD. Show all posts

Thursday, January 15, 2009

dm_skinny_specs ... trimming the fat from dm models' specs

Just released my 2nd gem dm_skinny_specs last night, this gem aims to help u trim away extra fat from dm models' specs. Here's a quick example of how things work (for 0.1.0):

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
require 'dm_skinny_spec'

class User
include DataMapper::Resource

property :id, Serial
property :name, String, :auto_validation => false
property :email, String, :auto_validation => false

validates_length :name, :min => 5
validates_format :email, :as => :email_address, :allow_nil => false
end

User.fix {{
:name => /\w{5,10}/.gen,
:email => /\w{10}/.gen + '@fmail.com',
}}

describe User do

before do
User.auto_migrate!
@instances = {}
end

# This MUST be provided since we do not mandate u to use any specific fixture
# utility.
def instance(id=:default)
@instances[id] ||= User.gen
end

it_should_validate_length :name, :min => 5
it_should_validate_format :email, :as => :email_address, :allow_nil => false

end


Hey !! Wait a minute, doesn't skinny_spec and the whole idea of these spec helpers sound familiar ?! Ok Ok, i admit it, i was inspired by the original skinny_spec for rails :P

Monday, November 24, 2008

Getting Selenium & Firefox3 to Work on Ubuntu

(BACKGROUND: I've upgraded my notebook & since i hope to focus more time on software development, this time round, i've decided to install something that just work out of the box, and i've chosen to install ubuntu instead of debian, despite being a debian user for the past 5 years.)

Since i'm a BDD fan these days, and i simply love writing cucumber's features, so there i was trying to get the bundled selenium example to run:

#$ git-clone git://github.com/aslakhellesoy/cucumber.git
#$ cd /examples/selenium
#$ rake features

And i got stack trace screaming "/usr/lib/ruby/1.8/net/http.rb:560:in `initialize': Connection refused - connect(2) (Errno::ECONNREFUSED)" ... OOPS, seems that i've forgotten to install selenium & run selenium:

#$ sudo gem install Selenium
#$ selenium

And i see the console displaying positive dump:
java  -jar /usr/lib/ruby/gems/1.8/gems/Selenium-1.1.14/lib/selenium/openqa/selenium-server.jar.txt 
20:47:29.661 INFO - Java: Sun Microsystems Inc. 1.6.0_0-b12
20:47:29.662 INFO - OS: Linux 2.6.27-7-generic amd64
...
So far so good, and rolling up my sleeves:

#$ rake features

After getting some green steps, i got a stack trace ... Switching to my selenium console shows:
java.lang.RuntimeException: Firefox couldn't be found in the path!
Please add the directory containing 'firefox-bin' to your PATH environment
variable, or explicitly specify a path to Firefox like this:
*firefox /blah/blah/firefox-bin
...
OK, its turns out that i need to specify the binary for firefox, as indicated by 'firefox-bin'. Having done some fishing round, i open up /examples/selenium/features/step_definitons/stories_steps.rb to edit the "*chrome" string to "*chrome /usr/lib/firefox-3.0.4/firefox-3.0", thus getting the following block:
Before do
@browser = Selenium::SeleniumDriver.new(
"localhost", 4444, "*chrome /usr/lib/firefox-3.0.4/firefox-3.0",
"http://localhost", 15000 )
@browser.start
end
Confident that things should work as expected this time, i did:

#$ rake features

Arrgghh, this time selenium console hangs at:
21:00:29.455 INFO - creating new remote session
21:00:29.627 INFO - Allocated session 202cd7534d044c81a66e027960e3d79b for http://localhost, launching...
21:00:29.745 INFO - Preparing Firefox profile...
After some googling round, i've found a solution, it turns out that the selenium bundled selenium-server.jar.txt (yup, the *.txt is intentional) needs some tweaking, and fortunately for me, there is already a one available for me to use, and i simply did:

#$ cd /usr/lib/ruby/gems/1.8/gems/Selenium-1.1.14/lib/selenium/openqa
#$ sudo mv selenium-server.jar.txt selenium-server.jar.txt.orig
#$ sudo wget http://edwardotis.net/public_files/selenium/selenium_server_FF3/selenium-server.jar

And after restarting my selenium, and running the rake tasks, i'm assured i have the environment for writing features using cucumber+selenium :] ... 3 cheers !!

Wednesday, November 12, 2008

Playing with Merb+Cucumber+Webrat (1)

In my previous post, we have setup the environment for playing with merb+cucumber+webrat. Now lets start playing !! Under ur merb project directory, place the following code in features/users.feature:
在之前的文章中,我们设好了我们的 merb+cucumber+webrat 的环境。现在,让我们开始玩玩吧!! 在你的 merb 项目中,把以下的代码复制进features/users.feature:
Feature: Manage Users
To have the system support multiple users
I should be able to manage users in the system

Scenario: View Existing Users
Given the following 5 users exist in the system:
| name | email |
| Peter | peter@abc.com |
| John | john@xyz.org |
| Jane | jane@efg.net |
| Mary | mary@hij.info |
| Cathy | cathy@stu.us |
When I go to /users
Then I should see all 5 users
And all users should be ordered by name
And I should see Peter having "Email" as "peter@abc.com"
Yup, pretty basic for any system with basic user support. To run the above feature, you can use one of the followings:
的确是非常简单的管理用户的 feature,接下来,让我们在终端中执行以下的指令:

#$ rake feature[/users]
#$ rake features

The difference is that the 1st command runs only the specified feature, while the later runs all. Usually when u are focusing on a feature, you will run the 1st command. In ur console, you will get the following output (i'm leaving out the colors to keep my life simple):
这两个指令的分别在于,前者只运行一个 feature 文件,而后者将运行所有的 features。当你正开发某个 feature 时,运行前者将帮你省下很多时间。让我们看看以下的终端的输出:

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/users.feature:6
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/users.feature:14
And all users should be ordered by name # features/users.feature:15
And I should see Peter having email "peter@abc.com" # features/users.feature:16

1 steps skipped
4 steps pending

You can use these snippets to implement pending steps:

Given /^the following 5 users exist in the system:$/ do
end

Then /^I should see all 5 users$/ do
end

Then /^all users should be ordered by name$/ do
end

Then /^I should see Peter having email "peter@abc.com"$/ do
end

Finished in 0.006268 seconds

0 examples, 0 failures

From your console, you will probably notice some very nice and intuitive colorings, and since they are very intuitive, i shall skip over them. Notice that for pending (yellow) steps, cucumber tries to be helpful by suggesting how you are going to write them. Now, let's take cucumber's hint, copy-and-paste those suggested lines, and open features/steps/users_steps.rb:
发现了没,输出的字句都用了非常直观的的颜色来帮你识别其中的意义,而我也不再这>里多加说明。看见了针对黄色的 pending 输出,cucumber 试着给了你一些提示,让你知道相关的 step 该怎么编写。那我们就接受它的提示,将这些提示代码复制到 features/steps/users_steps.rb:
Given /^the following 5 users exist in the system:$/ do
end
Then /^I should see all 5 users$/ do
end
Then /^all users should be ordered by name$/ do
end
And do:
接着运行:

#$ rake feature[/users]

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/steps/users_steps.rb:1
expected 0 block argument(s), got 1 (Cucumber::ArityMismatchError)
features/steps/users_steps.rb:1:in `/^the following 5 users exist in the system:$/'
features/users.feature:6:in `Given the following 5 users exist in the system:'
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/steps/users_steps.rb:4
And all users should be ordered by name # features/steps/users_steps.rb:7

1 steps failed
4 steps skipped

OH NO !! What have we done wrong ?? No worries, we are on the right track :]. The red failure lines are intended. Let's fix the problem by editing features/steps/users_steps.rb:
真糟糕,我们做错了什么吗??别担心,一切都在掌握中。红色失败是意料中的,让我们改改 features/steps/users_steps.rb 来搞定它吧:
Given /^the following 5 users exist in the system:$/ do |attrs_table|
end
Then /^I should see all 5 users$/ do
end
Then /^all users should be ordered by name$/ do
end
Then /^I should see Peter having "Email" as "peter@abc.com"$/ do
end
And do:
再来运行:

#$ rake feature[/users]

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/steps/users_steps.rb:1
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/steps/users_steps.rb:4
And all users should be ordered by name # features/steps/users_steps.rb:7
And I should see Peter having "Email" as "peter@abc.com" # features/steps/users_steps.rb:10

5 steps passed

OH NO !! What have we done wrong ?? Just adding attrs_table and we get a 'passed' for all steps ?? We've not written any model/view/controller code yet, so how can ... One key point to note here is that as long as we have a matching step, and the step doesn't trigger a failure, it is considered a 'passed'. Anyway, there is a workaround for this. Since cucumber does not yet support any pending/todo in a step, we can rewrite features/steps/users_steps.rb:
搞什么东东??怎么这下子我们都拿到了绿色的成功?我们好像连 model/view/controller 的代码都没写!!在这里,我们知道了,只要我们有个匹配的 step,不管>里头的内容是什么,只要没产生错误,cucumber 都会把它视为一个绿色成功的 step。就现在的 cucumber 版本,我们可以双点小聪明,修改一下 features/steps/users_steps.rb:
Given /^the following 5 users exist in the system:$/ do |attrs_table|
pending
end
Then /^I should see all 5 users$/ do
pending
end
Then /^all users should be ordered by name$/ do
pending
end
Then /^I should see Peter having "Email" as "peter@abc.com"$/ do
pending
end
And do:
再来运行:

#$ rake feature[/users]

Feature: Manage Users  # features/users.feature
To have the system support multiple users
I should be able to manage users in the system
Scenario: View Existing Users # features/users.feature:5
Given the following 5 users exist in the system: # features/steps/users_steps.rb:1
undefined local variable or method `pending' for # (NameError)
./features/steps/users_steps.rb:2:in `Given /^the following 5 users exist in the system:$/'
features/users.feature:6:in `Given the following 5 users exist in the system:'
When I go to /users # features/steps/common_webrat.rb:4
Then I should see all 5 users # features/steps/users_steps.rb:4
And all users should be ordered by name # features/steps/users_steps.rb:7
And I should see Peter having "Email" as "peter@abc.com" # features/steps/users_steps.rb:10

1 steps failed
4 steps skipped

Pretty ugly, are we back to square one? Not really, as long as we are well aware of the actual reason. Let's take a break take a look at how Aslak (cucumber's author) sums up the style of development with BDD and cucumber:
  1. when u have a new feature to implement, start by writing a new feature (a Feature in features/*.feature file) or scenario (a Scenario under an existing features/*.feature)
  2. describe the feature/scenario by filling in Then, followed by When, and finally Given
  3. run the features (make sure the new steps are yellow or red)
  4. write code to address the yellow/red steps
  5. repeat 3-4 to get all green steps
  6. when it comes to the nitty gritty details of your classes, use rspec to write specs before writing code
有点难看,我们又回到原点了吗?不一定,只要我们知道其背后的意义就暂时可以将就一下吧。让我们抛开代码,来看看 Aslak(cucumber 的作者)如何阐述使用 BDD 的开发模式和 cucumber 来开发::
  1. 当你有一个新的功能需要实现时,先把它写在一个新的 feature (即一个 features/*.feature 文件里的 Feature)或 scenario (在现有的 features/*.feature 添加一个 Scenario)
  2. 编写/形容一下这个新的 feature/scenario,先从 Then 着手,让后使用 When,最后才用 Given
  3. 运行 features (确保它们是红色或黄色的)
  4. 针对这些红色或黄色的 steps,编写相关的代码
  5. 重负 3-4,知道你看见所有的 steps 都成绿色
  6. 针对一些非常小的细节,建议使用 rspec,不过,记得要先写 specs,让后才些代码
I shall not go into the iterating of writing codes and running the features since this will well go into many many pages, but before i end this post, let's take a look at how we can rewrite features/steps/users_steps.rb to make it more generic, less coupled to users:
好啦,接下来就是完成这些 steps 的内容,让后再来写代码了。太烦琐了,我也不想花再多的篇幅来编写。不过,在我结束前,我们可以再看看 features/steps/users_steps.rb 里的 steps,或许可以改一改来让它们更不被 users 绑死:
Given /^the following (\d+) (\w+) exist in the system:$/ do | count, resource, attrs_table|
pending
end
Then /^I should see all (\d+) (\w+)$/ do | count, resources |
pending
end
Then /^all (\w+) should be ordered by (\w+)$/ do | resources, attribute |
pending
end
Then /^all (\w+) should be ordered by (\w+)$/ do | resources, attribute |
pending
end
Then /^I should see (\w+) having "(.*)" as "(.*)"$/ do | resource_nick, attribute, value |
pending
end
With the above, the steps are more generic and we can probably placed them into features/steps/common_result_steps.rb, and remove features/steps/users_steps.rb. Should we call it refactoring of our steps ?
不错吧,程序员就是应该这样,在合理的范围里找捷径!!而既然这些 steps 已不再和 users 那么的紧紧相连,我们可以把它们放在 features/steps/common_result_steps.rb 里。 这算不算 steps 的重构?

The sample codes for this exercise (tag 0.1.x) can be downloaded from:
如果你想看看我是如何完成这个练习的,你可以下载相关代码(tag 为 0.1.x):
http://github.com/ngty/ty_cucumber_salad

Monday, November 10, 2008

Setting Up Merb+Cucumber+Webrat

(BACKGROUND: Even though i've very much like to write rspec stories for my previous project, due to time constraint, i have eventually rely on the traditional click-and-observe on my browser, which i must admit is very INEFFICIENT and FRUSTRATING, and it makes me hate myself for resorting to such non-programmer approach. I HATE SUCH MANUAL TESTING. This time round, i'm starting on a new merb project and i swear to myself, i'm going to automate all tests !!! No more click-and-observe in my browser !! And i'm just in time for the new kid in the block ... CUCUMBER !!)
(背景:虽然之前我一直想尝试 rspec stories,可是因为时间的紧迫,最终我还是使用了传统的‘鼠标点击/眼睛观察’方式,我必须承认,这是非常没有效率和让我气恼的方式,特别我是个程序员!!我最憎恨的就是这种耗时间,>耗人力的测试方式。这一会,我有机会使用 merb 来开发新的项目,而这一回,我告诉自己,我要全自动化的测试!!而正好,cucumber 出现了!!)

How much different is cucumber from rspec's stories, i never know since i've never work with stories. But it should be better, given the fact that rspec is phasing out its stories in favour of it. Now, lets get our dev environment ready. By following this merb google group thread, these are the steps i've done to get things up and running:
其实到底 rspec stories 和 cucumber 有多大的差别,我不太晓得,毕竟我没用过前者。不过,cucumber 应该更好吧>,否则,rspec stories 也不会逐步让 cucumber 替代了。在我们开始玩 cucumber 前,让我们来准备一下我们的开发环境吧:

#$ gem install merb

While writing this post, the above installs merb-1.0. Next, we are going to install cucumber, webrat and merb_cucumber from the following sources:
在写这篇 blog 时,以上的指令将安装 merb-1.0。接下来,让我们安装 cucumber,webrat 和 merb_cucumber,这些的源代码可从以下 github 库中获取:

* git://github.com/brynary/webrat.git
* git://github.com/david/merb_cucumber.git
* git://github.com/aslakhellesoy/cucumber.git

To install:
安装:

#$ git-clone [project_src]
#$ cd [project_dir]
#$ rake install

At the point of this writing, the above installs cucumber-0.1.9, webrat-0.3.1 and merb_cucumber-0.3.
在写这篇 blog 时
,以上将安装:cucumber-0.1.9, webrat-0.3.1 和 merb_cucumber-0.3。

Just a quick check to ensure things are OK:
让我们确保一切 OK:

#$ merb-gen app playground
#$ cd playground
#$ merb-gen cucumber --session-type webrat
#$ rake features

And you should see something as followings (minus the difference in colors):
你将看见一下的输出(当让颜色将更鲜艳):

Feature: Login  # features/login.feature
To ensure the safety of the application
A regular user of the system
Must authenticate before using the app
Scenario: Failed Login # features/login.feature:6
Given I am not authenticated # features/steps/login_steps.rb:1
When I go to /login # features/steps/common_webrat.rb:4
And I fill in "login" with "i_dont_exist" # features/steps/common_webrat.rb:16
And I fill in "password" with "and_i_dont_have_a_password" # features/steps/common_webrat.rb:16
And I press "Log In" # features/steps/common_webrat.rb:8
Then the login request should fail # features/steps/common_result_steps.rb:13
Then I should see an error message # features/steps/common_result_steps.rb:9

7 steps passed

Finished in 0.042779 seconds

0 examples, 0 failures

Well, that's all for today. The next post will be Playing with Merb+Cucumber+Webrat (1).
好啦,今天就到此为止。下一篇 blog 将为 Playing with Merb+Cucumber+Webrat (1)。