MarsZ
2017.1
完整的 feature 包含了多個 request(controller) 的測試。
User story 改變了,但整合測試的程式碼沒有從結構上跟著改變。
即使寫了許多測試,關鍵功能沒有確實被測試出來。
應該從專案內移除的測試,沒有被移除。
過多的 unit test、過少的 integration test
CI build 過久。
程式碼和規格文件的更改不同步。
新加入的開發者難以迅速掌握核心功能。
棄用規格文件,改放在 PM 腦袋裡。
不再追求測試覆蓋率,而是追求「核心功能是否確實被測出來」
文件必須和測試綁在一起。
describe 'User login' do
...
describe 'False cases' do
it 'Invalid email format' do
....
end
end
end
rspec –format doc
User login
False cases
Invalid email format
SBE 和 GWT 並非互斥的選擇,而是在於用什麼方式達到目的
(最後我們沒有採用此方法,但分享其核心觀念給各位…)
Feature: User trades stocks
Scenario: User requests a sell before close of trading
Given I have 100 shares of MSFT stock
And I have 150 shares of APPL stock
And the time is before close of trading
When I ask to sell 20 shares of MSFT stock
Then I should have 80 shares of MSFT stock
And I should have 150 shares of APPL stock
And a sell order for 20 shares of MSFT stock should have been executed
為了把 feature 描述清楚,結果寫 scenario 時,有似曾相似感。
scenario 寫細一點用來區隔 feature 時,又好像把 GWT 寫一部分進來。
digraph hierarchy {
nodesep=1.0
a->b
a->c
b->d
d->dd
dd->dd1
dd->dd2
b->e
e->ee
e->eee
c->f
c->g
g->gg
c->h
h->hh
f->i
f->j
i->k
j->l
ee->ee1
ee1->ee2
ee1->ee3
dd1->ddd1
dd2->ddd2
eee->eee1
eee1->eee2
k->kk
l->ll
gg->ggg
ggg->ggg1
hh->hhh
hhh->hhh1
a [label="Feature"]
b [label="Feature"]
c [label="Feature"]
d [label="Scenario"]
e [label="Scenario"]
f [label="Scenario"]
g [label="Scenario"]
h [label="Scenario"]
dd [label="Given"]
ee [label="Given"]
eee [label="Given"]
i [label="Given"]
j [label="Given"]
gg [label="Given"]
hh [label="Given"]
dd1 [label="When"]
dd2 [label="When"]
ee1 [label="When"]
eee1 [label="When"]
k [label="When"]
l [label="When"]
ggg [label="When"]
hhh [label="When"]
ddd1 [label="Then"]
ddd2 [label="Then"]
ee2 [label="Then"]
ee3 [label="Then"]
eee2 [label="Then"]
kk [label="Then"]
ll [label="Then"]
ggg1 [label="Then"]
hhh1 [label="Then"]
}
rspec-given 算是比較接近我們當時的需求:「寫 code 的方式不往複雜方向走,又能讓 rpsec 的 doc 輸出符合 GWT 需要」
其實…
不用任何工具,我們也還是可以解決 BDD 上的需求 XD
describe 'Feature: 登入' do
describe 'Feature: FB' do
describe 'Scenario: User 尚未註冊以前,使用 FB 登入時,自動建立帳號並完成註冊' do
describe 'Given user table 為空' do
before { .... }
descrie 'When user 以 FB 登入' do
before { .... }
it 'Then user 登入成功' do
...
end
end
end
end
end
end
doc output:
Feature: 登入
Feature: FB
Scenario: User 尚未註冊以前,使用 FB 登入時,自動建立帳號並完成註冊
Given user table 為空
When user 以 FB 登入
Then user 登入成功
feature '登入' do
feature 'FB' do
Scenario 'User 尚未註冊以前,使用 FB 登入時,自動建立帳號並完成註冊' do
Given 'user table 為空' do
before { .... }
When 'user 以 FB 登入' do
before { .... }
Then 'user 登入成功' do
...
end
end
end
end
end
end
(output 不變)
(我們沒有 TDD)
xvfb-run -a bundle exec rake spec:nofeatures
bundle exec rubocop
git clone --branch gh-pages git@github.com:5fpro/jrf-sunny.git ./live-document
bundle exec rspec --format progress --format html --order defined --out ./live-document/"${CI_BRANCH}".html ./spec/features/
cd live-document
git add .
ts=$(date +"%Y-%m-%d %T")
git config user.name 'CodeShip'
git config user.email 'codeship@5fpro.com'
git commit -m "'CI at ${ts} --skip-ci'"
git push origin gh-pages
json="{\"text\":\"Live doc: http://5fpro.github.io/jrf-sunny/${CI_BRANCH}.html for ${CI_BRANCH}\",\"username\": \"CodeShip\",\"channel\": \"#ci-server\",\"icon_url\": \"https://a.slack-edge.com/7bf4/img/services/codeship_48.png\"}"
curl -H "Content-type: application/json" -X POST -d "${json}" https://hooks.slack.com/services/xxxxxxxxxx
lib/tasks/spec.rake
if Rails.env.development? || Rails.env.test?
namespace :spec do
RSpec::Core::RakeTask.new(:nofeatures) do |task|
file_list = FileList['spec/**/*_spec.rb']
%w(features).each do |exclude|
file_list = file_list.exclude("spec/#{exclude}/**/*_spec.rb")
end
task.pattern = file_list
end
end
end
(勇敢割掉非核心功能和情境吧)