原文放在我的博客中 http://scriptogr.am/mafai/post/cucumber-best-practices Title: Cucumber 经验总结 Tag: cucumber,auto test,selenium slug: cucumber-best-practices
使用一段时间 cucumber 了,也看过一些书 The cucumber book, rspec book, 翻过一些 github 上开源项目的 cucumber,再结合自己项目和团队项目的经验做个总结
不是必须,看实际情况。我个人是非常推荐使用。总要考虑几个问题
好处
是否由测试人员撰写,我个人是推荐由开发人员编写,但每个 feature 的定义和 Scenario 由产品经理/测试人员是定义。
可以采取由开发人员轮流坐庄写 cucumber,不过这个我没机会实践,所以不知道效果会如何。
写 rspec 时,好流行安装 guard,然后代码改变时,同时运行相应的 rspec,理论上 cucumber 也可以这样做,我反对这样做。
主要是 cucumber 运行的实在太慢,会严重打击你的积极性。另外,spork 和 guard spork 也是默认不对代码修改,运行 cucumber,这也是有道理的。
一般采用,对像加行为的描述,也用附加特定条件或范围的。也可以将行为放在前面。
[module][action][prep]_[condition].feature
Example:
user_signin_through_oauth_sina.feature
post_add.feature
post_delete_when_no_permission.feature
不要直接添加在 web_steps.rb
和 support/env.rb
了,非常快就 overload 失控
实际上 web_steps 已经不再默认生成。
可按功能拆分 steps:
user_steps post_steps blog_steps
分两种观点,一种利用 background,可以抽取公用到 background 一种是不写 background,每个 scenario 独立
这里没有定论,我编向是写 background,简单来讲,就是可重复使用前提条件,不好的地方就是降低可阅读性。但维护时会减轻工作量。如果不是为了生成的 cucumber 文档给产品经理看,我觉得还是使用 background 比较好。
将相同步骤 cucumber 变成一个 step,即复用你的 cucumber steps 网上有大量的讨论,关于使用哪个好,我个人偏好是 imperative,原因是,大量的 declarative steps 会造成维护困难,改起来非常累
可以看看这两篇
cucumber-imperative-or-declarative-that-is-the-question/
Imperative vs declarative scenarios in user story
Cucumber 的 steps 尽量用人类看得懂的语言,所有 正则表达式,css, xpath,等各种代码不应该在 cucumber steps 中出现。需要时,就另外写一个 steps 封装一下就好。
可以将按模块划分,将相应的 features 放入子目录,如果 features 少,也可以省略。但不要用 sprint11, 或 milestone11 这种目录,一定要的话,可以写在 tag 上,如 s@sprint11,否则维护起来就杯具了。
一种写法是 As xxx Role, I would do yyy so that zzz 这种写法会对项目新成员会有一定的帮助,不好的地方就是维护成本了,所以那几个开源项目都没有严格按照 User story 的写法,只是简单完成即可。我偏向是简单写,直观明了。
命名规范 [action]_[item].feature
没有子目录
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
大体都差不多和 teambox 一样, [module]_sptes.rb
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
[item]_[action].feature
我偏向这种,因为分类清楚点
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
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
features/activity_stream.feature
Feature: The activity stream Scenario: Sorting Given a user with username "bob" #没有使用 pickle When I try to sign in manually" #一起来看看 sign in 吧
features/step_definitions/session_steps.rb
When /^I try to sign in manually$/ do manual_login end
features/support/user_cuke_helpers.rb
# use the @me user to perform a manual login via the sign_in page def manual_login visit login_page login_as @me.username, @me.password #login_as 其实就是 wrap 了 fill_in 方法 end
features/support/paths.rb
def login_page path_to "the new user session page" end
def path_to(page_name) when /^the ([\w ]+) page$/ send("#{$1.gsub(/\W+/, '_')}_path")
不要再使用 app/assets 下的东西了。
➜ sample_files git:(dev) ls /Users/mafai/Projects/teambox/features/support/sample_files dragon.jpg tiger.jpg
这个应该是默认的了,The cucumber book 中,提及到为什么不用 webrat 和 selenium,在这里就不废话了。
这个 gem 帮你省确了大量的 module 创建的 steps,用上好,你的 steps 会减少好多,但同时,你的 cucumber 描述就变得不是那么容易阅读。 !注意,开源项目引入了这个 gem,但并不使用它,如果你想你的 cucumber steps 变得更简洁和易于阅读,pickle 不是你工具。
用上 factory girl 建立对像时,可以设定默认值或指定规则的 value,也可以省下不少代码
Pickle.configure do |config| #config.adapters = [:machinist] config.adapters = [:factory_girl] #以后通过 factory girl 来创建对像 #config.map 'I', 'myself', 'me', 'my', :to => 'user: "me"' end
#所有的 module 都须要在 FactoryGirl 登记一下,否则在运行时,pickle 会有问题,undefined steps FactoryGirl.define do factory :post do title { "Dummy title" } body { "#{title}!" } end end
spork cucumber
Spork 可以先 preload 整个测试环境
cucumber -r features features/posts.feature --drb
然后就享受一下速度吧,但注意这样做好,如果你将代码修改后,其实 spork 不会懂得 reload 的。
guard init spork
生成
/Users/mafai/Projects/compass/Guardfile
你会发觉,即使代码出错,也不会影响 cucumber 的测试,主要是默认缓存了 class 的原因
config.cache_classes = false
#改成 false 就好了
你也可以选择 watch 所有的*.rb 文件,restart spork server,不过这实践不是这些工具所提倡的。
使用 webkit 做前端测试,速度会提升,因为不需要打开 browser,只要在env.rb
用上Capybara.javascript_driver = :webkit
如果是一些标准命名的 page,再不须添加 path 了,否则真的很烦
else begin page_name =~ /the (.*) page/ path_components = $1.split(/\s+/) self.send(path_components.push('path').join('').to_sym) #这个 send 方法我不清楚是调用哪一个,其实就是将 user profile page,转换成 user_profile_path,这将其转成 symbol 字符串 rescue Object => e raise "Can't find mapping from \"#{page_name}\" to a path.\n" + "Now, go and add a mapping in #{FILE_}" end
cucumber features/billing/credit_card.feature:104 -f progress -r features
$ cucumber -f rerun --out rerun.txt
#结果 features/one.feature:367 features/another.feature:91:117
改一下 hook.rb
就好
Before do
$redis.flushall
end
After do
$redis.flushall
# clean out the Solr index after each scenario
Sunspot.remove_all!
end
见到一种较特别的写法,就是在测试环境时,动态添加一个 route 和 action,具体看 diaspora 项目的代码。其它都是模似用户的 steps 做的。
http://spin.atomicobject.com/2011/06/02/never-say-click-good-cucumber-system-testing-practices/
http://www.cowboycoded.com/2011/01/05/better-web-steps-for-cucumber-with-capybara/
https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit
http://robots.thoughtbot.com/post/189412598/five-ridiculously-awesome-cucumber-and-webrat
http://collectiveidea.com/blog/archives/2011/05/25/testing-with-sunspot-and-cucumber/
http://www.cowboycoded.com/2011/01/05/better-web-steps-for-cucumber-with-capybara/
http://blog.trydionel.com/2010/02/06/testing-sunspot-with-cucumber/