---
title: "基本語法：功能描述 - Cucumber 的文件測試法"
date: 2024-01-12T00:00:00+08:00
publishDate: 2024-01-12T00: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/12/test-with-cucumber-syntax-describe/"
language: "zh-tw"
---


Cucumber 的測試撰寫基本上是非常容易的，因為我們不是去配合程式來實現測試，而是直接去撰寫測試後，讓程式去配合測試。

<!--more-->

## Feature 檔案{#feature-file}

Cucumber 的語法格式被稱作為 Gherkin 同時習慣用 `.feature` 作為副檔名，並且習慣性的放在 `features/` 目錄下。

至於組織檔案的方式，比較多的是不開子目錄統一放到 `features/` 下面，因此只會有 `features/support` 和 `features/step_definitions` 兩個目錄分別用來對應測試環境的設定，以及根據測試的描述對應成實際的程式呼叫兩個目錄存在。

然而，如果覺得功能非常多且繁複的話，Cucumber 是會掃描所有 `features/` 下的任何目錄，只要副檔名是 `.feature` 都會被納入，可以根據需求去切分目錄來讓結構更清晰。


## 功能與場景{#feature-and-scenario}

在 Cucumber 中一個功能（Feature）就是一個檔案，我們會用場景（Scenario）區分不同情境下這個功能如何運作，因此我們可以規劃類似這樣的結構。

```gherkin
Feature: 會員註冊
  Scenario: 當開放註冊時，新會員 John 順利註冊
  Scenario: 當關閉註冊時，新會員 Bob 看到「不開放註冊」訊息
  # ...
```

一個功能會涵蓋多少內容，就需要仔細的思考，必要時也可以拆分成一些細項。舉例來說，會員功能之下還有註冊、登入、重設密碼等等不同的功能，如果處理的機制不複雜，我們可以統一放在 `features/user.feature` 裡面，然而如果每個功能都還有非常多不同的情境，那麼區分成 `features/user_login.feature` 會更好的維護這些規格。

我們可以把場景想像成 RSpec 的 `context` 或者 Jest 的 `describe` 程式碼區塊，主要是用來區分情境。

## 背景{#background}

在撰寫 Cucumber 測試的時候，要盡可能的追求交代清楚脈絡，也就是打開任何測試檔，都可以在沒有特定背景知識的前提下理解運作，然而我們可能會有這樣的情況發生。

```gherkin
Feature: 喜歡文章
  Scenario: 當 John 第一次點選「測試好棒」喜歡按鈕時時，喜歡次數增加
    Given 這裡有一篇文章 "測試好棒"
    # ...
  Scenario: 當 John 再次點選「測試好棒」的喜歡按鈕時，喜歡次數減少
    Given 這裡有一篇文章 "測試好棒"
    # ...
```

這個測試是針對特定文章進行的，因此所有的場景都會以某一篇文章作為前提，這個時候我們可以透過背景（Background）的交代，來減少重複的定義。

```gherkin
Feature: 喜歡文章
  Background:
    Given 這裡有一篇文章 "測試好棒"

  Scenario: 當 John 第一次點選「測試好棒」喜歡按鈕時時，喜歡次數增加
    # ...
  Scenario: 當 John 再次點選「測試好棒」的喜歡按鈕時，喜歡次數減少
    # ...
```

這個功能有點類似 RSpec 或者 Jest 的 `beforeEach` 機制，在 Cucumber 中我們基本上是無法用 Gherkin 語法來實現 `beforeAll` 的效果，因此也會預期每個場景都是獨立存在，在撰寫測試的時候盡量以這樣的角度下去思考，比較不會感到疑惑。

