Process 流程
是否采用
不是必须,看实际情况。我个人是非常推荐使用。总要考虑几个问题
- 投入成本,包括编写,和维护(保持更新),运行时间
- 效果,是否需要持续集成
好处
- 当要deploy前,绿色一大片的感觉让你觉得更有安全感。
- 自动化测试,减少大量的从复劳动
- 代码变更时,自动化测试可追踪代码变更是否对现有功能有所影响
谁来编写
是否由测试人员撰写,我个人是推荐由开发人员编写,但每个feature的定义和Scenario由产品经理/测试人员是定义。
Case 1测试人员编写
- 没再为功能性测试编写Test case
- 不能快速完成,因为steps需要了解背后实现(理论上不需要,但很多exceptional case)
- 需要开发人员支持,讲述给测试人员,一些steps的trick way
Case 2开发人员编写
- 估算时带来好处,不需要为测试人员等待开发人员完成代码的时间作估算
- 开发人员一般都不喜欢写测试用例,强制要求,也只能写出基本流程的测试(总比没有强)
结论
- 开发人员写cucumber,代码重用性和质量会高很多,效率也高
- 测试人员定制scenario,和定义测试用例test case的道理一样,会全面很多
其它可能性
可以采取由开发人员轮流坐庄写cucumber,不过这个我没机会实践,所以不知道效果会如何。
采用BDD?
写rspec时,好流行安装guard,然后代码改变时,同时运行相应的rspec,理论上cucumber也可以这样做,我反对这样做。
主要是cucumber运行的实在太慢,会严重打击你的积极性。另外,spork和guard spork也是默认不对代码修改,运行cucumber,这也是有道理的。
代码
Naming convention 文件命名规范
一般采用,对像加行为的描述,也用附加特定条件或范围的。也可以将行为放在前面。
[module]_[action]_[prep]_[condition].feature
Example:
user_signin_through_oauth_sina.feature
post_add.feature
post_delete_when_no_permission.feature
Steps 不要overload web_steps
不要直接添加在 web_steps.rb
和 support/env.rb
了,非常快就overload失控
实际上 web_steps 已经不再默认生成。
可按功能拆分steps:
user_steps
post_steps
blog_steps
是否使用Background
分两种观点,一种利用background ,可以抽取公用到background
一种是不写background,每个scenario 独立
这里没有定论,我编向是写background,简单来讲,就是可重复使用前提条件,不好的地方就是降低可阅读性。但维护时会减轻工作量。如果不是为了生成的cucumber文档给产品经理看,我觉得还是使用background比较好。
DRY 原则 (imperative or declarative)
将相同步骤cucumber 变成一个step,即复用你的cucumber steps
网上有大量的讨论,关于使用哪个好,我个人偏好是imperative,原因是,大量的declarative steps会造成维护困难,改起来非常累
可以看看这两篇
cucumber-imperative-or-declarative-that-is-the-question/
Imperative vs declarative scenarios in user story
Cucumber steps基本规范
Cucumber的steps尽量用人类看得懂的语言,所有 正则表达式,css, xpath,等各种代码不应该在cucumber steps中出现。需要时,就另外写一个steps封装一下就好。
文件目录结构,附开源项目的做法
可以将按模块划分,将相应的features放入子目录,如果features少,也可以省略。但不要用sprint11, 或milestone11这种目录,一定要的话,可以写在 tag 上,如s@sprint11,否则维护起来就杯具了。
是否按标准的User story写背景和description
一种写法是 As xxx Role, I would do yyy so that zzz
这种写法会对项目新成员会有一定的帮助,不好的地方就是维护成本了,所以那几个开源项目都没有严格按照User story的写法,只是简单完成即可。我偏向是简单写,直观明了。
diaspora
命名规范 [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
Step
大体都差不多和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
teambox
[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
Step
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
Cucumber样例
diaspora
-
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
-
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")
Cucumber 的测试附件存放位置
不要再使用app/assets下的东西了。
➜ sample_files git:(dev)
ls /Users/mafai/Projects/teambox/features/support/sample_files
dragon.jpg tiger.jpg
其它工具,cucumber好帮手
Capybara
这个应该是默认的了,The cucumber book中,提及到为什么不用webrat和selenium,在这里就不废话了。
Pickle
这个gem帮你省确了大量的module创建的steps,用上好,你的steps会减少好多,但同时,你的cucumber 描述就变得不是那么容易阅读。
!注意,开源项目引入了这个gem,但并不使用它,如果你想你的cucumber steps变得更简洁和易于阅读,pickle不是你工具。
Factory girl
用上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
spork cucumber
Spork 可以先preload 整个测试环境
cucumber -r features features/posts.feature --drb
然后就享受一下速度吧,但注意这样做好,如果你将代码修改后,其实spork不会懂得reload的。
Guard spork
guard init spork
生成
/Users/mafai/Projects/compass/Guardfile
你会发觉,即使代码出错,也不会影响cucumber的测试,主要是默认缓存了class的原因
config.cache_classes = false
#改成false就好了
你也可以选择 watch 所有的*.rb文件,restart spork server,不过这实践不是这些工具所提倡的。
使用webkit代替selenium
使用 webkit做前端测试,速度会提升,因为不需要打开browser,只要在env.rb
用上Capybara.javascript_driver = :webkit
Trick
默认路径 path
如果是一些标准命名的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
cucumber features/billing/credit_card.feature:104 -f progress -r features
Cucumber rerun 重复运行出错的scenario
$ 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
Devise登录
见到一种较特别的写法,就是在测试环境时,动态添加一个route和action,具体看diaspora项目的代码。其它都是模似用户的steps做的。