---
title: "基本語法：步驟定義 - Cucumber 的文件測試法"
date: 2024-01-26T00:00:00+08:00
publishDate: 2024-01-26T00:00:00+08:00
lastmod: 2023-12-06T15:50:15+08:00
tags: ["Cucumber","教學","測試","Gherkin","步驟"]
series: "test-with-cucumber"
toc: true
permalink: "https://blog.aotoki.me/posts/2024/01/26/test-with-cucumber-syntax-step-definition/"
language: "zh-tw"
---


使用 Cucumber 的 Gherkin 來撰寫測試是相當直覺的，然而這需要我們付出一些代價去實現「步驟定義」來將文件跟測試整合起來，然而這也是一個很好的機會讓我們去反思如何操作我們的軟體。

<!--more-->

## 步驟定義{#step-definition}

步驟定義是將我們在文件中像是 `When John 點選 "測試好棒"` 的句子提取出來，並且加以定義這個句子實際上該做些什麼，以 JavaScript 來說可以利用 Playwright 這類工具，而 Ruby 則是 Capybara 這類可以模擬瀏覽器操作的工具。

```ruby
When('{string} 點擊 {string}') do |user, text|
  click_on text
end
```

以 Ruby 為例子，我們使用 Cucumber 定義的 DSL（Domain Specific Language，領域特定語言） 把 `John` 和 `測試好棒` 設定成可以任意填寫的欄位，並且呼叫 Capybara 提供的 DSL 進行「點選」的動作。

如果換成 JavaScript 的版本，除了語言的語法差異外，基本上是相同的。

```typescript
When('{string} 點擊 {string}', async function (this: World, user: string text: string) {
  // ...
  await page.getByText(text).click();
});
```

如果是 `Then` 或者 `Given` 的情境，只需要替換內容即可，像是 `Then` 則是使用 `expect` 來做斷言。

```ruby
Then('我會看到 {string} 在畫面上') do |text|
  expect(page).to have_text(text)
end
```

除此之外，如果我們希望建立多筆測試資料，可以利用 Cucumber 的 Table 機制。

```gherkin
Feature: 文章列表
  Scenario: 列出文章
    Given 這裡有一些文章
      | title |
      | 文章一 |
      | 文章二 |
    # ...
```

像上面這樣的表格結構，可以利用 `#hashes` 方法進行讀取。

```ruby
Given('這裡有一些文章') do |table|
  table.hashes.each do |article|
    # ...
  end
end
```

## 場景案例{#example}

有時候我們會希望在同一個場景（Scenario）裡面用不同的數值來測試，這個時候可以利用 `Example`（案例）的功能來舉出不同的例子，使用方式類似步驟定義的表格。

```gherkin
Feature: 計算機
  Scenario: 加法
    When 輸入 <int1>
    And 輸入 +
    And 輸入 <int2>
    And 點選 "計算"
    Then 看到 <sum>

    Examples:
      | int1 | int2 | sum |
      | 1    | 2    | 3   |
      | 9    | 7    | 16  |
```

像這樣子，我們可以把舉例中會使用到的內容以 `<int1>` 這樣的格式標記，在運行測試的時候就會自然的替換進去，也就可以用不同的輸入來進行驗證。

## 參數類型{#parameter-type}

回到步驟定義的 `When John 點選 "測試好棒"` 這段描述，如果我們直接使用前面提到的步驟定義實際上會發生問題，這時候就需要透過指定參數類型來處理。

```ruby
When('{string} 點擊 {string}') do |user, text|
  click_on text
end
```

原因在於 `John` 的格式不符合字串（String）的類型定義，也就是使用 `"`（雙引號）標記，然而如果加上雙引號寫起來也不是那麼直覺，那麼利用參數類型（Parameter Type）來輔助就變得容易很多。

```ruby
# features/supports/env.rb

# ...

ParameterType(
  name: 'user',
  regexp: /[A-Za-z]+/,
  transformer: ->(name) { User.by_name(name) }
)
```

我們可以直接定義一個叫做 `user` 的類型，並且自動的從資料庫中找到這個物件，這樣在步驟定義中就可以像這樣子改寫。

```ruby
When('{user} 點擊 {string}') do |user, text|
  sign_in user # 以指定的使用者登入
  click_on text
end
```

在描述測試的時候，就能夠以更加直觀的方式進行，也更容易理解。

