我們現在已經準備好了可以使用 Cucumber 撰寫功能測試(Feature Test)的開發環境,接下來我們會用前端實作購物車的功能並且測試,然後接續實現後端完成一個具備非常基礎功能的前後端分離專案。
商品列表
一個購物車的基本要素,不外乎就是商品列表、購物車的管理這兩項功能,因此我們會先實現一個可以列出商品列表的機制,來讓我們可以實現「加入購物車」的功能。
使用 Vite 建立的專案會有一些預設的範例,在開始之前可以清除掉沒有使用到的檔案,以及我們驗證環境正常的
features/hello.feature
這類檔案。
首先,我們加入 features/products.feature
這個檔案,作為商品列表的測試檔案。
1#language:zh-TW
2功能: 商品列表
3 場景: 蒼時可以看到 Ruby 秘笈和 RSpec 秘笈兩本書
4 當 開啟網站
5 那麼 可以看見商品 "Ruby 秘笈"
6 並且 可以看見商品 "RSpec 秘笈"
我們的實作很簡單,只需要將兩項商品呈現出來就達到目的。
為了方便理解,這邊會使用
language:zh-TW
的標記讓 Cucumber 以中文方式撰寫。
因為「開啟網站」和「看間商品」這兩個步驟我們還沒有定義過,因此需要修改 features/step_definitions/common.ts
加入新的定義。
1import { When, Then } from "@cucumber/cucumber"
2import { expect } from "@playwright/test"
3
4import World from '../support/world'
5
6When('開啟網站', async function() {
7 await this.visit('/')
8})
9
10Then('可以看見商品 {string}', async function (this: World, targetText: string) {
11 await expect(this.page.getByText(targetText)).toBeVisible()
12});
基本上 Cucumber 這類測試會希望模擬「真實操作」狀況,因此大多會從首頁開始,同時 Vue 在我們的使用情境下也是 SPA(Single Page Application,單頁式應用)作為前提,因此只需要很簡單的實現 await this.visit('/')
來開啟前端畫面即可。
至於看見商品的實作,我們利用 Playwright 的定位器(Locator)機制,直接尋找我們希望查詢的文字(如:Ruby 秘笈)並且預期 toBeVisible()
(可見的)就能驗證我們可以在畫面上看見這個商品。
要通過這個測試也不困難,我們直接替換掉 src/App.vue
這個檔案的內容,直接寫上內容即可。
1<template>
2 <div>
3 <div>Ruby 秘笈</div>
4 <div>RSpec 秘笈</div>
5 </div>
6</template>
7
8<script setup lang="ts">
9</script>
試著運行 yarn run cucumber-js
會發現測試順利通過,在這個階段我們不需要去擔心資料的來源、畫面呈現或者最終的呈現效果,因為只有一個測試是無法完整的涵蓋這個功能。
加入購物車
在這一個階段,我們會希望可以有一個「加入購物車」的按鈕,並且在點選後改變計數的呈現,因此我們可以繼續增加測試。
1#language:zh-TW
2功能: 商品列表
3 # ...
4 場景: 蒼時可以對 Ruby 秘笈點選加入購物車,並看到商品數為 1
5 當 開啟網站
6 並且 把 "Ruby 秘笈" 加入購物車
7 那麼 可以看見購物車商品數為 1
看起來並不複雜,我們跟呈現商品列表一樣先將測試的步驟實現出來。
1import { When, Then } from "@cucumber/cucumber"
2import { expect } from "@playwright/test"
3
4import World from '../support/world'
5
6// ...
7
8When('把 {string} 加入購物車', async function(this: World, productName: string) {
9 const product = await this.page.getByTestId(/product-/).filter({ hasText: productName })
10 await product.getByRole('button', { name: '加入購物車' }).click()
11})
12
13// ...
14
15Then('可以看見購物車商品數為 {int}', async function(this: World, amount: number) {
16 await expect(this.page.getByTestId('cart')).toHaveText(`一共 ${amount} 項商品`)
17})
相對前面的步驟,這一次相對的複雜不少。首先我們需要可以識別「不同商品品項」在 Playwright 的 Locator 選項上,使用 CSS 選取並不算方便,這裡評估後認為最適合的是 testid
(測試識別)的模式,因此我們預期所有的商品都會有 product-1
這樣的 ID 被標記,那麼條件就是具備 product-
開頭並且包含了商品名稱的元素。
接下來找到這個元素中「加入購物車」的按鈕去點選他。
商品數量的斷言條件就相對容易,我們一樣用 testid
去找到具備 cart
名稱的元素,然後檢查內容具備 一共 ? 項商品
這樣的文字。
繼續更新 src/App.vue
的內容,來通過這個測試的要求。
1<template>
2 <div data-testid="cart">一共 1 項商品</div>
3 <div>
4 <div data-testid="product-1">
5 Ruby 秘笈
6 <button>加入購物車</button>
7 </div>
8 <div data-testid="product-2">
9 RSpec 秘笈
10 <button>加入購物車</button>
11 </div>
12 </div>
13</template>
14
15<script setup lang="ts">
16</script>
也許你會感到疑惑,沒有實作任何「邏輯」是沒問題的嗎?這是因為我們的測試描述的情境還不夠完善,因此會出現像這樣的狀況,這段實作確實能通過目前的測試的。
商品計數
很明顯的,前面一個步驟的實作是無法達到「統計購物車數量」的期待,我們可以透過增加新的測試條件來完善這個情境。
1#language:zh-TW
2功能: 商品列表
3 背景:
4 當 開啟網站
5
6 # ...
7
8 場景: 蒼時可以對 Ruby 秘笈點選加入購物車,並看到商品數為 1
9 並且 把 "Ruby 秘笈" 加入購物車
10 那麼 可以看見購物車商品數為 1
11
12 場景: 蒼時可以對 Ruby 秘笈和 RSpec 秘笈點選加入購物車,並看到商品數為 2
13 當 把 "Ruby 秘笈" 加入購物車
14 並且 把 "RSpec 秘笈" 加入購物車
15 那麼 可以看見購物車商品數為 2
因為「開啟網站」是所有場景都共用的,我們可以抽取出來變成背景資訊的描述,這裡我們增加了新的商品數量描述,來反應「商品數量變化」的要求。
接下來我們就可以修正 src/App.vue
的實作,讓購物車的商品計數可以反應出這樣的狀況。
1<template>
2 <div data-testid="cart">一共 {{ cart.count }} 項商品</div>
3 <div>
4 <div data-testid="product-1">
5 Ruby 秘笈
6 <button @click="onAddToCart">加入購物車</button>
7 </div>
8 <div data-testid="product-2">
9 RSpec 秘笈
10 <button @click="onAddToCart">加入購物車</button>
11 </div>
12 </div>
13</template>
14
15<script setup lang="ts">
16import { reactive } from 'vue'
17
18
19const cart = reactive({ count: 0 })
20const onAddToCart = () => cart.count++
21</script>
這個版本的實作,我們使用 Vue 3 的 reactive()
紀錄了一個物件的變化,裡面呈現了 cart
的計數,並且讓每個「加入購物車」按鈕都對應到 onAddToCart()
這個方法,用來對購物車的數量進行遞增,那麼就可以順利的呈現出數量變化的需求。
為了讓測試容易實現,我們盡量控制每一個階段的實作,讓實作變得簡單可控以及盡量跟著 Cucumber 所撰寫的文件拓展,逐步的完善,這樣也能更快地發現一些沒注意到的盲點。
如果對這篇文章有興趣,可以透過以下連結繼續閱讀這系列的其他文章。
- 同時完成測試與文件 - Cucumber 的文件測試法
- 基本語法:功能描述 - Cucumber 的文件測試法
- 基本語法:驗證行為 - Cucumber 的文件測試法
- 基本語法:步驟定義 - Cucumber 的文件測試法
- 基本語法:輔助設定 - Cucumber 的文件測試法
- 前端環境:Vite 與 Cucumber - Cucumber 的文件測試法
- 商品列表與加入購物車 - Cucumber 的文件測試法
- 重構與移出購物車 - Cucumber 的文件測試法
- 商品資料與總價 - Cucumber 的文件測試法
- 結帳與結果 - Cucumber 的文件測試法
- 整理前端實作 - Cucumber 的文件測試法
- 初始化後端專案 - Cucumber 的文件測試法
- 商品資料 API - Cucumber 的文件測試法
- 更新購物車 API - Cucumber 的文件測試法
- 加入資料模型 - Cucumber 的文件測試法
- 持久化保存 - Cucumber 的文件測試法
- 結帳處理 - Cucumber 的文件測試法
- 在 Rails 的前後端分離 - Cucumber 的文件測試法
- 匯入前端實作 - Cucumber 的文件測試法
- 重現後端實作 - Cucumber 的文件測試法
- 累積價值 - Cucumber 的文件測試法