蒼時弦也
蒼時弦也
資深軟體工程師

不用瀏覽器的前端開發

最近在準備明年(2024)的連載主題,在實作的時候意外發現自己逐漸習慣「不使用瀏覽器」的狀況下去進行開發,除了比較常用的後端情境之外,連前端也很順利的可以實踐這件事情。

測試驅動開發

一直到現在,我都很難實踐測試驅動開發。畢竟有非常多情況是很難適用的,例如在做 PoC(Proof of concept,概念驗證)的時候,還硬要寫測試是有點浪費時間,或者在對需求很不確定時也只能硬著頭皮做看看。

如果在需求足夠明確的狀況下,要實現這件事情就沒有預想中的困難。但這也不表示所有的開發過程都要先寫測試,採用 ATDD(Acceptance test-driven development,驗收測試驅動開發)的方式更為適合。

簡單來說,就是我們以「想要的成果」當作前提撰寫測試,並且順著這些測試來實作。在過程中會不斷的重構出新的元件或模組,他們的測試可能是在重構(Refactoring)的過程中實現,不一定會在初期完成。

舉個例子,像是我在後端預期有一個這樣的 API 回應:

1{
2  "data": [
3	  { "id": 1, "done": true, "description": "write article" }
4  ]
5}

那麼我可以先寫一段測試驗證,可以用 Cucumber、RSpec、Vitest 等等各種測試框架來實現。

1RSpec.describe 'GET /todos' do
2  # ...
3  subject { get todos_path }
4
5  it { is_expected.to include(/"data": \[/) }
6  it { is_expected.to include(/"id": 1/) }
7  # ...
8  # or customize matcher to verify response
9end

在這個階段,我們可以會有一些 Controller、Model 之類的物件被實現,但是我們還不需要去撰寫相關的單元測試(Unit Test)只需要先關注在這個功能的輸入與輸出即可。

前端的目標

這幾年對 OKR(Objective Key-Result)的概念比較熟悉後,會發在軟體產業的產品開發,不外乎就是去實現滿足使用者需求的功能,那麼在 ATDD 的思考方式下,我們只需要關注「如何使用達成目的」這樣的目標即可。

假設我們要製作一個計算機,可能會有一份這樣的 User Story(使用者故事,描述使用者的一系列動作)這裡直接使用 Cucumber 的 Gherkin 格式撰寫,現實狀況可能更像一段文章。

1#language:zh-TW
2功能: 計算機
3  場景: 蒼時可以透過點選 1 + 1 獲得 2 的結果
4 點選按鈕 "1"
5    並且 點選按鈕 "+"
6    並且 點選按鈕 "1"
7    並且 點選按鈕 "C"
8    那麼 可以看到 "2" 作為結果

這裡描述了一個加法計算的情況,並且詳細的描述我們從使用者故事中提取出來的「預期操作」來反應這件事情,最終的目標是有一段程式能實現這件事情。

過去我會覺得這樣的測試非常難以處理,主要是在環境準備的過程中有不少條件需要滿足。在準備文章一開始提到明年的連載時,也是在這個階段摸魚了一段時間才開始。然而,現在有非常多容易使用的工具,我們可以很輕鬆的實現這件事情。

用 Playwright 實現

選用 Playwright 的理由是我用起來覺得蠻順手的,如果習慣使用像是 Cypress 這類工具也完全沒問題。

我們這邊直接以 Playwright 的方式撰寫測試,如何跟 Cucumber 搭配就請期待我在 2024 年的新連載系列。

 1import { test, expect } from '@playwright/test';
 2
 3test('aotoki can calculate 1 + 1', async ({ page }) => {
 4  await page.getByRole('button', { name: '1' }).click()
 5  await page.getByRole('button', { name: '+' }).click()
 6  await page.getByRole('button', { name: '1' }).click()
 7  await page.getByRole('button', { name: 'C' }).click()
 8
 9  await expect(page.getByTestId('result')).toHaveText('2')
10});

上面這段測試程式,跟 Cucumber 描述的行為是完全一致,也就是說只要我們能在我們的前端實作中符合這些條件,那麼就可以實現這個功能,而這個功能在開發過程中是不需要「在瀏覽器測試」的。

以這個案例,我們是無法驗證有 1 以外的按鈕,因此我們可以繼續改善測試的描述。

 1#language:zh-TW
 2功能: 計算機
 3  場景: 蒼時可以透過點選 1 + 1 獲得 2 的結果
 4 點選按鈕 "<btn1>"
 5    並且 點選按鈕 "<btn2>"
 6    並且 點選按鈕 "<btn3>"
 7    並且 點選按鈕 "C"
 8    那麼 可以看到 "<res>" 作為結果
 9    例子:
10      | btn1 | btn2 | btn3 | res |
11      | 1    | +    | 1    | 2   |
12      | 2    | +    | 3    | 5   |
13      # ...

這件事情有幾個面向可以思考:

  • 使用者真的需要 2 ~ 9 的按鈕嗎?
  • 在目前這個階段有需要嗎?

因為不是這篇文章的重點,就留給大家思考。直到我們的功能完善後,我們在何時需要瀏覽器呢?主要就是「切版」的任務,這件事情有可能是由設計團隊負責,像是 Design System(設計系統)這樣的東西。

不論如何,假設我們試著將「畫面呈現」的因素排除專注在功能上,在需要處理「視覺」問題之前,也許都不需要使用瀏覽器輔助。

切版某種程度上也能做到自動化,然而要追求到精確的符合設計稿難度實在太高,我認為乖乖打開瀏覽器還是比較實用的方式。