本博客從Cucumber官方教程翻譯過來,因水平有限,翻譯有誤的地方請讀者不吝賜教。
以下是翻譯部分:
在這個快速教程中,你將學習如何:
-
安裝Cucumber
-
-
使用JavaScript寫第一個步驟定義(step definition)
-
運行Cucumber
-
學習BDD的基本工作流
我們將用Cucumber來開發一個可以辨別今天是否已經星期五的小型庫(library)。
在我們開始前,你需要以下工具:
-
Node.js
-
一個文本編輯器
打開終端,驗證Node.js已經恰當安裝了:
node -v
npm -v
這兩行命令各自都應該會打印出版本號。
創建一個空的Cucumber項目
我們通過創建一個新的文件夾和一個空的Node.js項目來開始。
mkdir hellocucumber cd hellocucumber npm init --yes
添加Cucumber作為開發的依賴
npm install cucumber --save -dev
在文本編輯器中打開package.json
,改變test
處,使它看起來像:
{ "name": "hellocucumber", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "cucumber-js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "cucumber": "^5.0.3" } }
准備文件結構
mkdir features
mkdir features/step_definitions
在項目的根目錄創建一個cucumber.js
文件,添加以下內容:
module.exports = { default: `--format-options '{"snippetInterface": "synchronous"}'` }
創建一個features/step_definitions/stepdefs.js
文件,添加以下內容:
const assert = require('assert');
const { Given, When, Then } = require('cucumber');
你現在就有了一個安裝了Cucumber的簡單項目。
驗證Cucumber安裝
為了確保每個組件在一起能夠正確工作,我們現在運行Cucumber
# Run via NPM
npm test
#Run standalone
./node_modules/.bin/cucumber-js
你應該能看見類似下面內容:
0 Scenarios
0 steps
0m00.000s
Cucumber的輸出告訴我們它沒有發現可以運行的場景。
寫一個Scenario
當我們用Cucumber來完成BDD時,我們使用具體的例子(example)來指定我們想要軟件完成什么功能。場景(Scenario)要在產品代碼之前編寫。它們(指場景)以可執行規范(executable specification)的身份作為它們生命周期的開始。當產品代碼做出來后,場景就承擔起活文件(living documentation)和自動化測試(automated tests)的角色。
在Cucumber中,一個實例(example)被稱為一個場景(Scenario)。場景在.feature
文件中定義, 該文件放在features
文件夾或其子文件夾中。
一個具體的實例(concrete example)是星期天不是星期五。
創建一個名為features/is_it_friday_yet.feature
空文件,添加以下內容:
Feature: Is it Friday yet? Everybody wants to know when it's Friday Scenario: Sunday isn't Friday Given today is Sunday When I ask whether it's Friday yet Then I should be told "Nope"
這個文件的第一行以關鍵字Feature:
開頭,后跟着一個名字。使用和文件名一樣的名字比較好。
第二行是對特征(feature)的簡要介紹。Cucumber不會運行這行,它只起到文檔記錄的作用。
第四行中Scenario: Sunday is not Friday
是一個場景(Scenario
),就是一個描述了軟件表現什么行為的具體實例(concrete example
)。
最后以關鍵字Given
,When
和Then
開頭的三行是四個場景的步驟(step
)。這里是Cucumber將會執行的地方。
看到提示scenario是undefined
既然我們有了一個場景,我們可以讓Cucumber執行它。
npm test
Cucumber將會告訴我們,有一個undefined的場景和三個undefined的步驟。同時Cucumber還會建議我們用一些代碼片段來定義(define
)這三個步驟:
UUU
Warnings:
1) Scenario: Sunday is not Friday # features/is_it_friday_yet.feature:4
? Given today is Sunday
Undefined. Implement with the following snippet:
Given('today is Sunday', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
? When I ask whether it's Friday yet
Undefined. Implement with the following snippet:
When('I ask whether it\'s Friday yet', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
? Then I should be told "Nope"
Undefined. Implement with the following snippet:
Then('I should be told {string}', function (string) {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
1 Scenario (1 undefined)
3 steps (3 undefined)
0m00.000s
復制這三個undefined步驟的代碼片段,把它們粘到features/step_definitions/stepdefs.js
。
看到提示scenario是pending
重新運行Cucumber。這次輸出有一點不一樣:
P--
Warnings:
1) Scenario: Sunday is not Friday # features/is_it_friday_yet.feature:4
? Given today is Sunday # features/step_definitions/stepdefs.js:3
Pending
- When I ask whether it's Friday yet # features/step_definitions/stepdefs.js:8
- Then I should be told "Nope" # features/step_definitions/stepdefs.js:13
1 Scenario (1 pending)
3 steps (1 pending, 2 skipped)
0m00.001s
Cucumber發現了我們的一個步驟定義並且執行了它。它們被標識為pending
,意味着我們需要讓它們做一些有意義的事情。
看到提示scenario為failing
下一步就是完成在步驟定義的注釋告訴我們要做的事情。
Write code here that turns the phrase above into concrete actions
嘗試在步驟的代碼中使用同樣的單詞。
把你的步驟定義代碼變成這樣:
const assert = require('assert'); const { Given, When, Then } = require('cucumber'); function isItFriday(today) { // We'll leave the implementation blank for now } Given('today is Sunday', function () { this.today = 'Sunday'; }); When('I ask whether it\'s Friday yet', function () { this.actualAnswer = isItFriday(this.today); }); Then('I should be told {string}', function (expectedAnswer) { assert.equal(this.actualAnswer, expectedAnswer); });
重新運行Cucumber:
..F
Failures:
1) Scenario: Sunday is not Friday # features/is_it_friday_yet.feature:4
✔ Given today is Sunday # features/step_definitions/stepdefs.js:8
✔ When I ask whether it's Friday yet # features/step_definitions/stepdefs.js:12
✖ Then I should be told "Nope" # features/step_definitions/stepdefs.js:16
AssertionError [ERR_ASSERTION]: undefined == 'Nope'
at World.<anonymous> (/private/tmp/tutorial/hellocucumber/features/step_definitions/stepdefs.js:17:10)
1 Scenario (1 failed)
3 steps (1 failed, 2 passed)
進步了!前兩個步驟通過了,但是最后一個失敗了。
看到提示scenario為passing
讓我們完成可能的最簡單的事情來讓場景得以通過。在這個例子中,只需要讓函數返回Nope
就可以了:
function isItFriday(today) { return 'Nope'; }
重新運行Cucumber:
...
1 Scenario (1 passed)
3 steps (3 passed)
0m00.003s
恭喜你!你已經得到你的第一個Cucumber全綠的場景。
添加另一個failing測試
下一個要測試的東西是,當今天是星期五的時候我們應該得到結果為正確。
更新is-it-friday-yet.feature
文件:
Feature: Is it Friday yet? Everybody wants to know when it's Friday Scenario: Sunday isn't Friday Given today is Sunday When I ask whether it's Friday yet Then I should be told "Nope" Scenario: Friday is Friday Given today is Friday When I ask whether it's Friday yet Then I should be told "TGIF"
我們需要增加一個步驟定義來設置today
為Friday:
Given('today is Friday', function () { this.today = 'Friday'; });
當我們運行這個測試時,將會失敗:
.....F
Failures:
1) Scenario: Friday is Friday # features/is_it_friday_yet.feature:9
✔ Given today is Friday # features/step_definitions/stepdefs.js:8
✔ When I ask whether it's Friday yet # features/step_definitions/stepdefs.js:16
✖ Then I should be told "TGIF" # features/step_definitions/stepdefs.js:20
AssertionError [ERR_ASSERTION]: 'Nope' == 'TGIF'
+ expected - actual
-Nope
+TGIF
at World.<anonymous> (/private/tmp/tutorial/hellocucumber/features/step_definitions/stepdefs.js:21:10)
2 scenarios (1 failed, 1 passed)
6 steps (1 failed, 5 passed)
這是因為我們還沒有完成邏輯部分!讓我們接下來完成這個工作。
通過
我們應該更新我們的代碼,讓它真的去驗證today
是否等於Friday
:
function isItFriday(today) { if (today === "Friday") { return "TGIF"; } else { return "Nope"; } }
重新運行Cucumber:
......
2 scenarios (2 passed)
6 steps (6 passed)
0m00.002s
使用變量和實例
眾所周知,一個星期除了星期天和星期五外還有其它天。讓我們使用變量來更新場景,然后驗證更多的可能性。我們將會使用變量和實例來驗證星期五,星期天和任意其它天。
更新is-it-friday-yet.feature
文件。注意當我們開始使用多個實例(Examples
)時,我們是如何從單個場景(Scenario
)到場景大綱(Scenario Outline
)的:
Feature: Is it Friday yet? Everybody wants to know when it's Friday Scenario Outline: Today is or is not Friday Given today is "<day>" When I ask whether it's Friday yet Then I should be told "<answer>" Examples: | day | answer | | Friday | TGIF | | Sunday | Nope | | anything else! | Nope |
我們需要用一個讀取<day>
字符串的值的步驟定義來替換原來的步驟定義中的today is Sunday
和today is Friday
兩處地方。更新stepdefs.js
文件,像下面這樣:
const assert = require('assert'); const { Given, When, Then } = require('cucumber'); function isItFriday(today) { if (today === "Friday") { return "TGIF"; } else { return "Nope"; } } Given('today is {string}', function (givenDay) { this.today = givenDay; }); When('I ask whether it\'s Friday yet', function () { this.actualAnswer = isItFriday(this.today); }); Then('I should be told {string}', function (expectedAnswer) { assert.equal(this.actualAnswer, expectedAnswer); });
重新運行Cucumber:
.........
3 scenarios (3 passed)
9 steps (9 passed)
0m00.001s
重構
既然我們有了可以工作的代碼,我們應該做一些重構工作:
-
我們應該把
isItFriday
函數從測試代碼中移到生成代碼中 -
我們可以從步驟定義中的某些地方提取一些
helper
方法,這樣在分布在幾個地方的函數都可以使用。
總結